Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b82538e62 | ||
|
|
3f17bda786 | ||
|
|
e10d7260c2 | ||
|
|
409cb2beb8 | ||
|
|
742ab17a2a | ||
|
|
9f254df091 | ||
|
|
290dcf0610 | ||
|
|
0f0290d574 | ||
|
|
5bb72dfe5d | ||
|
|
0b73a66516 | ||
|
|
0631fd2440 | ||
|
|
1e2e8650e1 | ||
|
|
21035b0c1a | ||
|
|
c884955ff7 | ||
|
|
b91ff430db | ||
|
|
b09c50bf9c | ||
|
|
7f0603effa | ||
|
|
ff441d3b3e | ||
|
|
950f4a8672 | ||
|
|
95eb81467c | ||
|
|
55ed883648 | ||
|
|
0c2cebb775 | ||
|
|
645588902b | ||
|
|
881e923105 | ||
|
|
ef5ca0c218 | ||
|
|
406b394704 | ||
|
|
7b0d8c2e77 | ||
|
|
b1b68ceedb | ||
|
|
6df3940c26 | ||
|
|
238476bfcd | ||
|
|
d69a9d52a9 | ||
|
|
b3007c03bd |
@@ -17,3 +17,9 @@ RELEASE
|
||||
deb
|
||||
lib/auto/gui.files.go
|
||||
snapcraft.yaml
|
||||
prime/
|
||||
snap/
|
||||
parts/
|
||||
stage/
|
||||
*.snap
|
||||
*.bz2
|
||||
|
||||
@@ -15,6 +15,7 @@ Alexander Graf (alex2108) <register-github@alex-graf.de>
|
||||
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
|
||||
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
|
||||
Andrew Dunham (andrew-d) <andrew@du.nham.ca>
|
||||
Andrew Rabert (nvllsvm) <ar@nullsum.net>
|
||||
Andrey D (scienmind) <scintertech@cryptolab.net>
|
||||
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
|
||||
Antony Male (canton7) <antony.male@gmail.com>
|
||||
@@ -57,6 +58,7 @@ Gilli Sigurdsson (gillisig) <gilli@vx.is>
|
||||
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
|
||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
||||
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
|
||||
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
|
||||
Jacek Szafarkiewicz (hadogenes) <szafar@linux.pl>
|
||||
Jake Peterson (acogdev) <jake@acogdev.com>
|
||||
@@ -65,6 +67,7 @@ James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github
|
||||
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
|
||||
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
||||
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>
|
||||
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
||||
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
|
||||
|
||||
+55
-74
@@ -1,92 +1,73 @@
|
||||
## Conduct
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming
|
||||
environment for all, regardless of gender, sexual orientation,
|
||||
disability, ethnicity, religion, or similar personal characteristic.
|
||||
## Our Pledge
|
||||
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames
|
||||
that might detract from a friendly, safe and welcoming environment for
|
||||
all.
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
## Our Standards
|
||||
|
||||
* Respect that people have differences of opinion and that every design
|
||||
or implementation choice carries a trade-off and numerous costs. There
|
||||
is seldom a right answer.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Please keep unstructured critique to a minimum. If you have solid
|
||||
ideas you want to experiment with, make a fork and see how it works.
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
* We will exclude you from interaction if you insult, demean or harass
|
||||
anyone. That is not welcome behaviour. We interpret the term
|
||||
"harassment" as including the definition in the <a
|
||||
href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>;
|
||||
if you have any lack of clarity about what might be included in that
|
||||
concept, please read their definition. In particular, we don't
|
||||
tolerate behavior that excludes people in socially marginalized
|
||||
groups.
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* Private harassment is also unacceptable. No matter who you are, if you
|
||||
feel you have been or are being harassed or made uncomfortable by a
|
||||
community member, please contact one of the channel ops or any of the
|
||||
Syncthing core team immediately. Whether you're a regular contributor
|
||||
or a newcomer, we care about making this community a safe place for
|
||||
you and we've got your back.
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
* Likewise any spamming, trolling, flaming, baiting or other
|
||||
attention-stealing behaviour is not welcome.
|
||||
## Our Responsibilities
|
||||
|
||||
## Moderation
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
These are the policies for upholding our community's standards of
|
||||
conduct in our communication channels, most notably in Syncthing-related
|
||||
IRC channels and on the web forum.
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
1. Remarks that violate the Syncthing standards of conduct, including
|
||||
hateful, hurtful, oppressive, or exclusionary remarks, are not
|
||||
allowed. (Cursing is allowed, but never targeting another user, and
|
||||
never in a hateful manner.)
|
||||
## Scope
|
||||
|
||||
2. Remarks that moderators find inappropriate, whether listed in the
|
||||
code of conduct or not, are also not allowed.
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
## Enforcement
|
||||
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked
|
||||
out of the communication channel to cool off.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at security@syncthing.net. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
5. If the user comes back and continues to make trouble, they will be
|
||||
banned, i.e., indefinitely excluded.
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
6. Moderators may choose at their discretion to un-ban the user if it
|
||||
was a first offense and they offer the offended party a genuine
|
||||
apology.
|
||||
## Attribution
|
||||
|
||||
7. If a moderator bans someone and you think it was unjustified, please
|
||||
take it up with that moderator, or with a different moderator, **in
|
||||
private**. Complaints about bans in-channel are not allowed.
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
8. Moderators are held to a higher standard than other community
|
||||
members. If a moderator creates an inappropriate situation, they
|
||||
should expect less leeway than others.
|
||||
|
||||
In the Syncthing community we strive to go the extra step to look out
|
||||
for each other. Don't just aim to be technically unimpeachable, try to
|
||||
be your best self. In particular, avoid flirting with offensive or
|
||||
sensitive issues, particularly if they're off-topic; this all too
|
||||
often leads to unnecessary fights, hurt feelings, and damaged trust;
|
||||
worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the
|
||||
urge to be defensive. Just stop doing what it was they complained about
|
||||
and apologize. Even if you feel you were misinterpreted or unfairly
|
||||
accused, chances are good there was something you could've communicated
|
||||
better — remember that it's your responsibility to make your fellow
|
||||
community members comfortable. Everyone wants to get along and we are
|
||||
all here first and foremost because we want to talk about cool
|
||||
technology. You will find that people will be eager to assume good
|
||||
intent and forgive as long as you earn their trust.
|
||||
|
||||
*Adapted from the [Rust Code of Conduct](https://github.com/rust-lang/rust/wiki/Note-development-policy#conduct)*
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling)*
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
+5
-6
@@ -18,15 +18,14 @@ RUN apk add --no-cache ca-certificates
|
||||
|
||||
COPY --from=builder /go/src/github.com/syncthing/syncthing/syncthing /bin/syncthing
|
||||
|
||||
RUN echo 'syncthing:x:1000:1000::/var/syncthing:/sbin/nologin' >> /etc/passwd \
|
||||
&& echo 'syncthing:!::0:::::' >> /etc/shadow \
|
||||
&& chown syncthing /var/syncthing
|
||||
RUN apk add --no-cache su-exec
|
||||
|
||||
USER syncthing
|
||||
ENV STNOUPGRADE=1
|
||||
ENV PUID=1000
|
||||
ENV PGID=1000
|
||||
|
||||
HEALTHCHECK --interval=1m --timeout=10s \
|
||||
CMD nc -z localhost 8384 || exit 1
|
||||
|
||||
ENTRYPOINT ["/bin/syncthing", "-home", "/var/syncthing/config", "-gui-address", "0.0.0.0:8384"]
|
||||
|
||||
ENTRYPOINT chown $PUID:$PGID /var/syncthing \
|
||||
&& su-exec $PUID:$PGID /bin/syncthing -home /var/syncthing/config -gui-address 0.0.0.0:8384
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ The exposed volumes are by default:
|
||||
|
||||
You can add more folders and map them as you prefer.
|
||||
|
||||
Note that Syncthing runs as UID 1000 in the container. This UID must have
|
||||
permission to read and modify the files in the containers.
|
||||
Note that Syncthing runs as UID 1000 and GID 1000 by default. These may be
|
||||
altered with the ``PUID`` and ``PGID`` environment variables.
|
||||
|
||||
Example usage:
|
||||
|
||||
|
||||
@@ -618,6 +618,8 @@ func buildSnap(target target) {
|
||||
snaparch := goarch
|
||||
if snaparch == "armhf" {
|
||||
goarch = "arm"
|
||||
} else if snaparch == "i386" {
|
||||
goarch = "386"
|
||||
}
|
||||
snapver := version
|
||||
if strings.HasPrefix(snapver, "v") {
|
||||
@@ -628,9 +630,10 @@ func buildSnap(target target) {
|
||||
snapgrade = "stable"
|
||||
}
|
||||
err = tmpl.Execute(f, map[string]string{
|
||||
"Version": snapver,
|
||||
"Architecture": snaparch,
|
||||
"Grade": snapgrade,
|
||||
"Version": snapver,
|
||||
"HostArchitecture": runtime.GOARCH,
|
||||
"TargetArchitecture": snaparch,
|
||||
"Grade": snapgrade,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -738,12 +741,11 @@ func listFiles(dir string) []string {
|
||||
|
||||
func rebuildAssets() {
|
||||
os.Setenv("SOURCE_DATE_EPOCH", fmt.Sprint(buildStamp()))
|
||||
runPipe("lib/auto/gui.files.go", "go", "run", "script/genassets.go", "gui")
|
||||
runPipe("cmd/strelaypoolsrv/auto/gui.go", "go", "run", "script/genassets.go", "cmd/strelaypoolsrv/gui")
|
||||
runPrint("go", "generate", "github.com/syncthing/syncthing/lib/auto", "github.com/syncthing/syncthing/cmd/strelaypoolsrv/auto")
|
||||
}
|
||||
|
||||
func lazyRebuildAssets() {
|
||||
if shouldRebuildAssets("lib/auto/gui.files.go", "gui") || shouldRebuildAssets("cmd/strelaypoolsrv/auto/gui.go", "cmd/strelaypoolsrv/auto/gui") {
|
||||
if shouldRebuildAssets("lib/auto/gui.files.go", "gui") || shouldRebuildAssets("cmd/strelaypoolsrv/auto/gui.files.go", "cmd/strelaypoolsrv/auto/gui") {
|
||||
rebuildAssets()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AudriusButkevicius/cli"
|
||||
)
|
||||
@@ -23,6 +24,12 @@ func init() {
|
||||
Requires: &cli.Requires{},
|
||||
Action: generalStatus,
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "Configuration",
|
||||
Requires: &cli.Requires{},
|
||||
Action: generalConfiguration,
|
||||
},
|
||||
{
|
||||
Name: "restart",
|
||||
Usage: "Restart syncthing",
|
||||
@@ -70,6 +77,15 @@ func generalStatus(c *cli.Context) {
|
||||
fmt.Println("Config in sync")
|
||||
}
|
||||
|
||||
func generalConfiguration(c *cli.Context) {
|
||||
response := httpGet(c, "system/config")
|
||||
var jsResponse interface{}
|
||||
json.Unmarshal(responseToBArray(response), &jsResponse)
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(jsResponse)
|
||||
}
|
||||
|
||||
func generalVersion(c *cli.Context) {
|
||||
response := httpGet(c, "system/version")
|
||||
version := make(map[string]interface{})
|
||||
|
||||
@@ -1 +1 @@
|
||||
gui.go
|
||||
gui.files.go
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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/.
|
||||
|
||||
//go:generate go run ../../../script/genassets.go -o gui.files.go ../gui
|
||||
|
||||
// Package auto contains auto generated files for web assets.
|
||||
package auto
|
||||
+10
-2
@@ -1497,13 +1497,16 @@ func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
|
||||
"size": f.Size,
|
||||
"permissions": fmt.Sprintf("%#o", f.Permissions),
|
||||
"deleted": f.Deleted,
|
||||
"invalid": f.Invalid,
|
||||
"invalid": protocol.FileInfo(f).IsInvalid(),
|
||||
"ignored": protocol.FileInfo(f).IsIgnored(),
|
||||
"mustRescan": protocol.FileInfo(f).MustRescan(),
|
||||
"noPermissions": f.NoPermissions,
|
||||
"modified": protocol.FileInfo(f).ModTime(),
|
||||
"modifiedBy": f.ModifiedBy.String(),
|
||||
"sequence": f.Sequence,
|
||||
"numBlocks": len(f.Blocks),
|
||||
"version": jsonVersionVector(f.Version),
|
||||
"localFlags": f.LocalFlags,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1516,11 +1519,16 @@ func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) {
|
||||
"size": f.Size,
|
||||
"permissions": fmt.Sprintf("%#o", f.Permissions),
|
||||
"deleted": f.Deleted,
|
||||
"invalid": f.Invalid,
|
||||
"invalid": db.FileInfoTruncated(f).IsInvalid(),
|
||||
"ignored": db.FileInfoTruncated(f).IsIgnored(),
|
||||
"mustRescan": db.FileInfoTruncated(f).MustRescan(),
|
||||
"noPermissions": f.NoPermissions,
|
||||
"modified": db.FileInfoTruncated(f).ModTime(),
|
||||
"modifiedBy": f.ModifiedBy.String(),
|
||||
"sequence": f.Sequence,
|
||||
"numBlocks": nil, // explicitly unknown
|
||||
"version": jsonVersionVector(f.Version),
|
||||
"localFlags": f.LocalFlags,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -702,7 +702,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
||||
dbFile := locations[locDatabase]
|
||||
ldb, err := db.Open(dbFile)
|
||||
if err != nil {
|
||||
l.Fatalln("Cannot open database:", err, "- Is another copy of Syncthing already running?")
|
||||
l.Fatalln("Error opening database:", err)
|
||||
}
|
||||
|
||||
if runtimeOptions.resetDeltaIdxs {
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Идентификатор на устройство",
|
||||
"Device Identification": "Идентификатор на устройство",
|
||||
"Device Name": "Име на устройството",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Устройство, което последно промени обекта",
|
||||
"Devices": "Устройства",
|
||||
"Disabled": "Деактивирано",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Локално състояние (общо)",
|
||||
"Log": "Доклад",
|
||||
"Log tailing paused. Click here to continue.": "Докладът е замразен. Натиснете, за да продължите.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Доклади",
|
||||
"Major Upgrade": "Основно Обновяване",
|
||||
"Mass actions": "Действия за всички",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Споделяне с устройства",
|
||||
"Share this folder?": "Сподели тази папка?",
|
||||
"Shared With": "Споделена с",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Покажи идентификатора",
|
||||
"Show QR": "Покажи QR",
|
||||
"Show diff with previous version": "Показване на разликите спрямо предната версия",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID del dispositiu",
|
||||
"Device Identification": "Identificació del dispositiu",
|
||||
"Device Name": "Nom del dispositiu",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "El dispositiu que va modificar el item per última vegada",
|
||||
"Devices": "Dispositius",
|
||||
"Disabled": "Desactivat",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Estat Local (Total)",
|
||||
"Log": "Registre",
|
||||
"Log tailing paused. Click here to continue.": "Pausada l'adició de dades al registre. Polsa ací per continuar.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Registres",
|
||||
"Major Upgrade": "Actualització important",
|
||||
"Mass actions": "Accions en masa",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Compartir amb els dispositius",
|
||||
"Share this folder?": "Compartir aquesta carpeta?",
|
||||
"Shared With": "Compartit amb",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR",
|
||||
"Show diff with previous version": "Mostrar les diferències amb la versió prèvia",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID přístroje",
|
||||
"Device Identification": "Identifikace přístroje",
|
||||
"Device Name": "Jméno přístroje",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Poslední přístroj, který modifikoval položku",
|
||||
"Devices": "Přístroje",
|
||||
"Disabled": "Vypnuto",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Místní status (Celkem)",
|
||||
"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 tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logy",
|
||||
"Major Upgrade": "Důležitá aktualizace",
|
||||
"Mass actions": "Hromadné akce",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Sdílet s přístroji",
|
||||
"Share this folder?": "Sdílet tento adresář?",
|
||||
"Shared With": "Sdíleno s",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Zobrazit ID",
|
||||
"Show QR": "Zobrazit QR",
|
||||
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Enheds-ID",
|
||||
"Device Identification": "Enhedsidentifikation",
|
||||
"Device Name": "Enhedsnavn",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Enheder",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokal tilstand (total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Ny version",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Del med enhed",
|
||||
"Share this folder?": "Del denne mappe",
|
||||
"Shared With": "Delt med",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Vis ID",
|
||||
"Show QR": "Vis QR",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Gerätekennung",
|
||||
"Device Identification": "Geräteidentifikation",
|
||||
"Device Name": "Gerätename",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
|
||||
"Devices": "Geräte",
|
||||
"Disabled": "Deaktiviert",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokaler Status (Gesamt)",
|
||||
"Log": "Protokoll",
|
||||
"Log tailing paused. Click here to continue.": "Protokollaufzeichnungen sind pausiert. Klicken Sie hier um fortzufahren.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Protokolle",
|
||||
"Major Upgrade": "Hauptversionsaktualisierung",
|
||||
"Mass actions": "Massenaktionen",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Teile mit diesen Geräten",
|
||||
"Share this folder?": "Diesen Ordner teilen?",
|
||||
"Shared With": "Geteilt mit",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Eigene Kennung",
|
||||
"Show QR": "Zeige QR Code",
|
||||
"Show diff with previous version": "Unterschied zur vorherigen Version anzeigen",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Ταυτότητα συσκευής",
|
||||
"Device Identification": "Ταυτότητα συσκευής",
|
||||
"Device Name": "Όνομα συσκευής",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Συσκευή από την οποία πραγματοποιήθηκε η τελευταία τροποποίηση του στοιχείου",
|
||||
"Devices": "Συσκευές",
|
||||
"Disabled": "Απενεργοποιημένη",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Τοπική κατάσταση (συνολικά)",
|
||||
"Log": "Αρχείο καταγραφής",
|
||||
"Log tailing paused. Click here to continue.": "Η αυτόματη κύλιση έχει διακοπεί. Πατήστε εδώ για να συνεχιστεί.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Αρχεία καταγραφής",
|
||||
"Major Upgrade": "Σημαντική αναβάθμιση",
|
||||
"Mass actions": "Μαζικές ενέργειες",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Διαμοίρασε με αυτές τις συσκευές",
|
||||
"Share this folder?": "Να διαμοιραστεί αυτός ο φάκελος;",
|
||||
"Shared With": "Διαμοιράζεται με",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Εμφάνιση ταυτότητας",
|
||||
"Show QR": "Δείξε τον κωδικό QR",
|
||||
"Show diff with previous version": "Εμφάνιση διαφορών με προηγούμενη έκδοση",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Device ID",
|
||||
"Device Identification": "Device Identification",
|
||||
"Device Name": "Device Name",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Devices",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Local State (Total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Major Upgrade",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Share With Devices",
|
||||
"Share this folder?": "Share this folder?",
|
||||
"Shared With": "Shared With",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Show ID",
|
||||
"Show QR": "Show QR",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
"Local State (Total)": "Local State (Total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Major Upgrade",
|
||||
"Mass actions": "Mass actions",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Aparato ID",
|
||||
"Device Identification": "Identigo de Aparato",
|
||||
"Device Name": "Nomo de Aparato",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Aparatoj",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Loka Stato (Tuta)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Ĉefa Ĝisdatigo",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Komunigu Kun Aparatoj",
|
||||
"Share this folder?": "Komunigi ĉi tiun dosierujon?",
|
||||
"Shared With": "Komunigita Kun",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Montru ID",
|
||||
"Show QR": "Montru QR",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Dispositivos",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Estado Local (Total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Actualización importante",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Compartir con dispositivos",
|
||||
"Share this folder?": "¿Deseas compartir esta carpeta?",
|
||||
"Shared With": "Compartir con",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Dispositivo que modificó por última vez el ítem",
|
||||
"Devices": "Dispositivos",
|
||||
"Disabled": "Deshabilitado",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Estado Local (Total)",
|
||||
"Log": "Registro",
|
||||
"Log tailing paused. Click here to continue.": "Seguimiento del registro pausado. Haga clic aquí para continuar.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Registros",
|
||||
"Major Upgrade": "Actualización importante",
|
||||
"Mass actions": "Acción masiva",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Compartir con dispositivos",
|
||||
"Share this folder?": "¿Deseas compartir esta carpeta?",
|
||||
"Shared With": "Compartir con",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR",
|
||||
"Show diff with previous version": "Mostrar la diferencia con la versión anterior",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Tresnaren ID-a",
|
||||
"Device Identification": "Tresnaren identifikazioa",
|
||||
"Device Name": "Tresnaren izena",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Tresnak",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lekuko egoera (Osoa)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Eguneratze nagusia",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Tresnekin partekatu",
|
||||
"Share this folder?": "Partekatze hau onartzen duzu?",
|
||||
"Shared With": "...ekin partekatua",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Erakutsi ene ID-a",
|
||||
"Show QR": "Erakutsi QR-a",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID de l'appareil",
|
||||
"Device Identification": "Identifiant de l'appareil",
|
||||
"Device Name": "Nom de l'appareil",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Appareil",
|
||||
"Disabled": "Disabled",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "État local (Total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Mise à jour majeure",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Synchroniser avec des appareils",
|
||||
"Share this folder?": "Acceptez-vous ce partage ?",
|
||||
"Shared With": "Synchronisé avec",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Afficher mon ID",
|
||||
"Show QR": "Afficher l'image QR",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID de l'appareil",
|
||||
"Device Identification": "Identifiant de l'appareil",
|
||||
"Device Name": "Nom convivial local de l'appareil",
|
||||
"Device rate limits": "Limitations de débit",
|
||||
"Device that last modified the item": "Dernier appareil modificateur",
|
||||
"Devices": "Appareils",
|
||||
"Disabled": "Désactivé",
|
||||
@@ -109,7 +110,7 @@
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Quand ils sont remplacés ou supprimés par Syncthing, les fichiers sont déplacés et horodatés vers le sous-répertoire .stversions dans une arborescence relative identique à celle de l'original.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Les fichiers sont protégés des changements réalisés sur les autres appareils, mais les changements réalisés sur celui-ci seront transférés aux autres.",
|
||||
"Filesystem Notifications": "Notifications du système de fichiers",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filesystem Watcher Errors": "Erreurs de la surveillance du système de fichiers",
|
||||
"Filter by date": "Filtrer par date",
|
||||
"Filter by name": "Filtrer par nom",
|
||||
"Folder": "Partage",
|
||||
@@ -118,7 +119,7 @@
|
||||
"Folder Path": "Chemin racine du partage",
|
||||
"Folder Type": "Type de partage",
|
||||
"Folders": "Partages",
|
||||
"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.": "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.",
|
||||
"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.": "Une erreur s'est produite pour les partages suivants lors du démarrage de la surveillance des changements. Une nouvelle tentative a lieu toutes les minutes, et les erreurs devraient disparaître rapidement. Si elles persistent, essayez de corriger le problème sous-jacent et demandez de l'aide si vous n'y parvenez pas.",
|
||||
"Full Rescan Interval (s)": "Intervalle de réanalyse complète (s)",
|
||||
"GUI": "Interface graphique",
|
||||
"GUI Authentication Password": "Mot de passe d'authentification GUI",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "État local (Total)",
|
||||
"Log": "Journal",
|
||||
"Log tailing paused. Click here to continue.": "La file d'attente du journal est en pause. Cliquer ici pour continuer.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Le fil du journal est en pause. Faites défiler vers le bas pour continuer.",
|
||||
"Logs": "Journaux",
|
||||
"Major Upgrade": "Mise à jour majeure",
|
||||
"Mass actions": "Actions multiples",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Synchroniser avec des appareils",
|
||||
"Share this folder?": "Acceptez-vous ce partage ?",
|
||||
"Shared With": "Participant(s)",
|
||||
"Sharing": "Partage",
|
||||
"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",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Apparaat-ID",
|
||||
"Device Identification": "Apparaatidentifikaasje",
|
||||
"Device Name": "Apparaatnamme",
|
||||
"Device rate limits": "Apparaatfluggenslimiet",
|
||||
"Device that last modified the item": "Apparaat dat dit item it lêst oanpast hat",
|
||||
"Devices": "Apparaten",
|
||||
"Disabled": "Utskeakele",
|
||||
@@ -109,7 +110,7 @@
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Triemen wurde ferset nei in mei datum stimpele ferzjes yn in .stversions map wannear troch Syncthing ferfangen of fuortsmiten.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Triemen binne ymmún foar feroarings makke troch oare apparaten, mar feroarings makke op dit apparaat wurde nei de rest fan 'e bondel ferstjoerd.",
|
||||
"Filesystem Notifications": "Meldingen fan Triemsysteem",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filesystem Watcher Errors": "Triemsysteemsjoggerflaters",
|
||||
"Filter by date": "Op datum filterje",
|
||||
"Filter by name": "Op namme filterje",
|
||||
"Folder": "Map",
|
||||
@@ -118,7 +119,7 @@
|
||||
"Folder Path": "Map-paad",
|
||||
"Folder Type": "Maptype",
|
||||
"Folders": "Mappen",
|
||||
"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.": "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.",
|
||||
"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.": "By it starten fan it sjen foar feroarings is der foar de folgende mappen is in flater ûnstean. It wurd eltse minuut opnij besike, dus de flaters koene wol ris gau fuortgean. At se der bliuwe, besykje dan it ûnderlizzende probleem te ferhelpen en freegje om help dat dit net slagget.",
|
||||
"Full Rescan Interval (s)": "Folledich wersken ynterfal (s)",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "Wachtwurd foar ferifikaasje yn GUI",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokale tastân (Folledich)",
|
||||
"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.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Lochs",
|
||||
"Major Upgrade": "Wichtige fernijing",
|
||||
"Mass actions": "Massa-aksjes",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Diele mei apparaten",
|
||||
"Share this folder?": "Dizze map diele?",
|
||||
"Shared With": "Dielt mei",
|
||||
"Sharing": "Dielen",
|
||||
"Show ID": "ID sjen litte",
|
||||
"Show QR": "QR sjen litte",
|
||||
"Show diff with previous version": "Ferskil (diff) mei de foarige ferzje sjen litte",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Eszközazonosító",
|
||||
"Device Identification": "Eszközazonosító",
|
||||
"Device Name": "Eszköz neve",
|
||||
"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",
|
||||
"Disabled": "Letiltva",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Helyi állapot (teljes)",
|
||||
"Log": "Napló",
|
||||
"Log tailing paused. Click here to continue.": "Napló utolsó sorainak figyelése leállítva. Ide kattintva lehet folytatni.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Napló utolsó sorainak figyelése leállítva. Görgetve lehet folytatni.",
|
||||
"Logs": "Naplófájlok",
|
||||
"Major Upgrade": "Főverzió-frissítés",
|
||||
"Mass actions": "Tömeges műveletek",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Megosztás más eszközzel",
|
||||
"Share this folder?": "Megosztható ez a mappa?",
|
||||
"Shared With": "Megosztva ezekkel:",
|
||||
"Sharing": "Megosztás",
|
||||
"Show ID": "Azonosító megjelenítése",
|
||||
"Show QR": "QR-kód megjelenítése",
|
||||
"Show diff with previous version": "Előző verzió eltérésének megjelenítése",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID Dispositivo",
|
||||
"Device Identification": "Identificazione Dispositivo",
|
||||
"Device Name": "Nome Dispositivo",
|
||||
"Device rate limits": "Limiti di velocità del dispositivo",
|
||||
"Device that last modified the item": "Dispositivo che ha modificato l'elemento per ultimo",
|
||||
"Devices": "Dispositivi",
|
||||
"Disabled": "Disabilitato",
|
||||
@@ -109,7 +110,7 @@
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "I file sostituiti o eliminati da Syncthing vengono datati e spostati in una cartella .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.": "I file sono protetti dalle modifiche effettuate negli altri dispositivi, ma le modifiche effettuate in questo dispositivo verranno inviate anche al resto del cluster.",
|
||||
"Filesystem Notifications": "Notifiche del filesystem",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filesystem Watcher Errors": "Errori nel monitoraggio del filesystem",
|
||||
"Filter by date": "Filtra per data",
|
||||
"Filter by name": "Filtra per nome",
|
||||
"Folder": "Cartella",
|
||||
@@ -118,7 +119,7 @@
|
||||
"Folder Path": "Percorso Cartella",
|
||||
"Folder Type": "Tipo di Cartella",
|
||||
"Folders": "Cartelle",
|
||||
"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.": "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.",
|
||||
"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.": "Per le seguenti cartelle si è verificato un errore durante l'avvio della ricerca delle modifiche. Sarà ripetuto ogni minuto, quindi gli errori potrebbero risolversi presto. Se persistono, prova a risolvere il problema sottostante e chiedi aiuto se non puoi.",
|
||||
"Full Rescan Interval (s)": "Intervallo di scansione completa (s)",
|
||||
"GUI": "Interfaccia Grafica Utente",
|
||||
"GUI Authentication Password": "Password dell'Interfaccia Grafica",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Stato Locale (Totale)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Visualizzazione log in pausa. Clicca qui per continuare.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Log",
|
||||
"Major Upgrade": "Aggiornamento Principale",
|
||||
"Mass actions": "Azioni di massa",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Condividi con i Dispositivi",
|
||||
"Share this folder?": "Vuoi condividere questa cartella?",
|
||||
"Shared With": "Condiviso Con",
|
||||
"Sharing": "Condivisione",
|
||||
"Show ID": "Mostra ID",
|
||||
"Show QR": "Mostra QR",
|
||||
"Show diff with previous version": "Mostra le differenze con la versione precedente",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "デバイスID",
|
||||
"Device Identification": "デバイスID",
|
||||
"Device Name": "デバイス名",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "デバイス",
|
||||
"Disabled": "無効",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "ローカル状態 (合計)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "メジャーアップグレード",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "共有するデバイス",
|
||||
"Share this folder?": "このフォルダーを共有しますか?",
|
||||
"Shared With": "共有中のデバイス",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "IDを表示",
|
||||
"Show QR": "QRコードを表示",
|
||||
"Show diff with previous version": "前バージョンとの差分を表示",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "기기 ID",
|
||||
"Device Identification": "기기 식별자",
|
||||
"Device Name": "기기 이름",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "항목을 마지막으로 수정 한 기기",
|
||||
"Devices": "기기",
|
||||
"Disabled": "비활성화",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "로컬 상태 (합계)",
|
||||
"Log": "기록",
|
||||
"Log tailing paused. Click here to continue.": "기록 출력이 일시정지됨. 계속하려면 여기를 클릭하십시오. ",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "기록",
|
||||
"Major Upgrade": "메이저 업데이트",
|
||||
"Mass actions": "Mass actions",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "공유할 기기",
|
||||
"Share this folder?": "이 폴더를 공유하시겠습니까?",
|
||||
"Shared With": "~와 공유",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "내 기기 ID",
|
||||
"Show QR": "QR 코드 보기",
|
||||
"Show diff with previous version": "이전 버전과 변경사항 보기",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Įrenginio ID",
|
||||
"Device Identification": "Įrenginio identifikacija",
|
||||
"Device Name": "Įrenginio pavadinimas",
|
||||
"Device rate limits": "Įrenginio spartos apribojimai",
|
||||
"Device that last modified the item": "Įrenginys, kuris paskutinis modifikavo elementą",
|
||||
"Devices": "Įrenginiai",
|
||||
"Disabled": "Išjungta",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Vietinė būsena (Bendrai)",
|
||||
"Log": "Žurnalas",
|
||||
"Log tailing paused. Click here to continue.": "Žurnalo galas pristabdytas. Spustelėkite čia, norėdami tęsti.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Žurnalo galas pristabdytas. Slinkite į apačią, norėdami tęsti.",
|
||||
"Logs": "Žurnalai",
|
||||
"Major Upgrade": "Stambus atnaujinimas",
|
||||
"Mass actions": "Masiniai veiksmai",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Dalintis su įrenginiais",
|
||||
"Share this folder?": "Dalintis šiuo aplanku?",
|
||||
"Shared With": "Dalinamasi su",
|
||||
"Sharing": "Dalinamasis",
|
||||
"Show ID": "Rodyti ID",
|
||||
"Show QR": "Rodyti QR",
|
||||
"Show diff with previous version": "Rodyti skirtumus su ankstesne versija",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Enhets-ID",
|
||||
"Device Identification": "Enhetskjennemerke",
|
||||
"Device Name": "Navn på enhet",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Enheten som sist endret elementet",
|
||||
"Devices": "Enheter",
|
||||
"Disabled": "Avskrudd",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokal tilstand (total)",
|
||||
"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.",
|
||||
"Logs": "Logger",
|
||||
"Major Upgrade": "Storoppgradering",
|
||||
"Mass actions": "Massehandlinger",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Del med enheter",
|
||||
"Share this folder?": "Del denne mappa?",
|
||||
"Shared With": "Delt med",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Vis ID",
|
||||
"Show QR": "Vis QR-kode",
|
||||
"Show diff with previous version": "Vis diff med forrige version",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"A device with that ID is already added.": "Apparaat-ID reeds toegevoegd.",
|
||||
"A negative number of days doesn't make sense.": "Een negatief aantal dagen is niet zinvol.",
|
||||
"A new major version may not be compatible with previous versions.": "Een nieuwe major version is misschien niet compatibel met eerdere versies.",
|
||||
"A device with that ID is already added.": "Er is al een apparaat met die ID toegevoegd.",
|
||||
"A negative number of days doesn't make sense.": "Een negatief aantal dagen heeft geen zin.",
|
||||
"A new major version may not be compatible with previous versions.": "Een nieuwe hoofdversie is mogelijk niet compatibel met eerdere versies.",
|
||||
"API Key": "API-sleutel",
|
||||
"About": "Over",
|
||||
"Action": "Actie",
|
||||
@@ -10,9 +10,9 @@
|
||||
"Add Device": "Apparaat toevoegen",
|
||||
"Add Folder": "Map toevoegen",
|
||||
"Add Remote Device": "Extern apparaat toevoegen",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Apparaten van het introductieapparaat aan onze lijst met apparaten toevoegen, voor gemeenschappelijk gedeelde mappen.",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Apparaten van het introductie-apparaat aan onze lijst met apparaten toevoegen, voor gemeenschappelijk gedeelde mappen.",
|
||||
"Add new folder?": "Nieuwe map toevoegen?",
|
||||
"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.": "Het interval van volledige scan zal daarbij ook vermeerderd worden (maal 60, dit is een nieuwe standaard van 1 uur). U kunt het later voor elke map manueel configureren nadat u nee kiest.",
|
||||
"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.": "Het interval van volledige scan zal daarbij ook vermeerderd worden (maal 60, dit is een nieuwe standaardwaarde van 1 uur). U kunt het later voor elke map manueel configureren nadat u nee kiest.",
|
||||
"Address": "Adres",
|
||||
"Addresses": "Adressen",
|
||||
"Advanced": "Geavanceerd",
|
||||
@@ -24,34 +24,34 @@
|
||||
"Alphabetic": "Alfabetisch",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map.",
|
||||
"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.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map. Als het pad naar de toepassing spaties bevat, moet dit tussen aanhalingstekens geplaatst worden.",
|
||||
"An external command handles the versioning. It has to remove the file from the synced folder.": "Een extern commando regelt het versiebeheer. Dit moet het bestand verwijderen van de gesynchroniseerde map.",
|
||||
"An external command handles the versioning. It has to remove the file from the synced folder.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gesynchroniseerde map.",
|
||||
"Anonymous Usage Reporting": "Anonieme gebruikersstatistieken",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wilt u naar het nieuwe formaat overschakelen?",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Apparaten die geconfigureerd worden op een introductieapparaat zullen ook aan dit apparaat worden toegevoegd.",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Apparaten die geconfigureerd worden op een introductie-apparaat zullen ook aan dit apparaat worden toegevoegd.",
|
||||
"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?",
|
||||
"Auto Accept": "Automatisch aanvaarden",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisch bijwerken biedt nu de keuze tussen stabiele uitgaven en uitgavekandidaten.",
|
||||
"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",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatisch mappen die dit apparaat aankondigt aanmaken of delen in de standaardlocatie.",
|
||||
"Available debug logging facilities:": "Beschikbare debuglogmogelijkheden:",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatisch mappen die dit apparaat aankondigt aanmaken of delen op de standaardlocatie.",
|
||||
"Available debug logging facilities:": "Beschikbare debuglog-mogelijkheden:",
|
||||
"Be careful!": "Wees voorzichtig!",
|
||||
"Bugs": "Bugs",
|
||||
"CPU Utilization": "CPU-gebruik",
|
||||
"Changelog": "Logboek",
|
||||
"Clean out after": "Schoon op na",
|
||||
"Changelog": "Wijzigingenlogboek",
|
||||
"Clean out after": "Opruimen na",
|
||||
"Click to see discovery failures": "Klikken om ontdekkingsproblemen weer te geven",
|
||||
"Close": "Sluiten",
|
||||
"Command": "Commando",
|
||||
"Comment, when used at the start of a line": "Reageer indien gebruikt aan het begin van een lijn.",
|
||||
"Command": "Opdracht",
|
||||
"Comment, when used at the start of a line": "Opmerking, wanneer gebruikt aan het begin van een regel",
|
||||
"Compression": "Compressie",
|
||||
"Configured": "Geconfigureerd",
|
||||
"Connection Error": "Verbindingsfout",
|
||||
"Connection Type": "Soort verbinding",
|
||||
"Connections": "Verbindingen",
|
||||
"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.": "Continu opvolgen voor wijzigingen is nu beschikbaar in Syncthing. Dit zal wijzigingen op een schijf detecteren en een scan uitvoeren op alleen de gewijzigde paden. De voordelen zijn dat wijzigingen sneller doorgevoerd worden en dat minder volledige scans nodig zijn.",
|
||||
"Copied from elsewhere": "Gekopieerd vanaf elders",
|
||||
"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.": "Continu opvolgen van wijzigingen is nu beschikbaar in Syncthing. Dit zal wijzigingen op een schijf detecteren en een scan uitvoeren op alleen de gewijzigde paden. De voordelen zijn dat wijzigingen sneller doorgevoerd worden en dat minder volledige scans nodig zijn.",
|
||||
"Copied from elsewhere": "Gekopieerd van ergens anders",
|
||||
"Copied from original": "Gekopieerd van het origineel",
|
||||
"Copyright © 2014-2016 the following Contributors:": "Auteursrecht © 2014-2016 voor de volgende bijdragers:",
|
||||
"Copyright © 2014-2017 the following Contributors:": "Auteursrecht © 2014-2017 voor de volgende bijdragers:",
|
||||
@@ -61,10 +61,11 @@
|
||||
"Default Folder Path": "Standaardmaplocatie",
|
||||
"Deleted": "Verwijderd",
|
||||
"Device": "Apparaat",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Apparaat \"{{name}}\" ({{device}} at {{address}}) wil verbinden. Wilt u dit toestaan?",
|
||||
"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",
|
||||
"Device Identification": "Apparaat-identificatie",
|
||||
"Device Name": "Naam apparaat",
|
||||
"Device rate limits": "Snelheidsbegrenzingen apparaat",
|
||||
"Device that last modified the item": "Apparaat dat het item laatst gewijzigd heeft",
|
||||
"Devices": "Apparaten",
|
||||
"Disabled": "Uitgeschakeld",
|
||||
@@ -73,7 +74,7 @@
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodiek scannen uitgeschakeld en instellen van opvolgen van wijzigingen mislukt. Elke minuut wordt opnieuw geprobeerd:",
|
||||
"Disconnected": "Niet verbonden",
|
||||
"Discovered": "Ontdekt",
|
||||
"Discovery": "Ontdekken",
|
||||
"Discovery": "Ontdekking",
|
||||
"Discovery Failures": "Ontdekkingsproblemen",
|
||||
"Do not restore": "Niet herstellen",
|
||||
"Do not restore all": "Niet alles herstellen",
|
||||
@@ -81,33 +82,33 @@
|
||||
"Documentation": "Documentatie",
|
||||
"Download Rate": "Downloadsnelheid",
|
||||
"Downloaded": "Gedownload",
|
||||
"Downloading": "Bezig met downloaden",
|
||||
"Edit": "Bewerk",
|
||||
"Downloading": "Downloaden",
|
||||
"Edit": "Bewerken",
|
||||
"Edit Device": "Apparaat bewerken",
|
||||
"Edit Folder": "Map bewerken",
|
||||
"Editing": "Bezig met bewerken",
|
||||
"Editing {%path%}.": "Bewerken van {{path}}.",
|
||||
"Editing": "Bewerken",
|
||||
"Editing {%path%}.": "{{path}} bewerken.",
|
||||
"Enable NAT traversal": "NAT traversal inschakelen",
|
||||
"Enable Relaying": "Relaying inschakelen",
|
||||
"Enabled": "Ingeschakeld",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Voer een positief getal in (bijv. \"2.35\") en selecteer een eenheid. Percentages zijn als deel van de totale schijfgrootte.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Voer een niet-toegewezen poortnummer in (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Voer door komma's gescheiden (\"tcp://ip:port\", \"tcp://host:port\") adressen in of voer \"dynamisch\" in om automatische ontdekking van het adres uit te voeren.",
|
||||
"Enter ignore patterns, one per line.": "Voer negeerpatronen in, één per regel.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Voer een niet-gereserveerd poortnummer in (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Voer door komma's gescheiden (\"tcp://ip:port\", \"tcp://host:port\") adressen in of \"dynamic\" om automatische ontdekking van het adres uit te voeren.",
|
||||
"Enter ignore patterns, one per line.": "Negeerpatronen invoeren, één per regel.",
|
||||
"Error": "Fout",
|
||||
"External File Versioning": "Extern versiebeheer voor bestanden",
|
||||
"External File Versioning": "Extern versiebeheer",
|
||||
"Failed Items": "Mislukte items",
|
||||
"Failed to load ignore patterns": "Laden van negeerpatronen mislukt",
|
||||
"Failed to setup, retrying": "Instellen mislukt, opnieuw proberen",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Als er geen IPv6-connectiviteit is worden problemen bij verbinden met IPv6-servers verwacht.",
|
||||
"File Pull Order": "Volgorde ontvangen bestanden",
|
||||
"File Pull Order": "Volgorde voor binnenhalen van bestanden",
|
||||
"File Versioning": "Versiebeheer",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Toegangsrechten voor bestanden worden genegeerd bij het zoeken naar wijzigingen. Gebruik voor FAT-bestandssystemen.",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Bits voor bestandsmachtigingen worden genegeerd bij het zoeken naar wijzigingen. Gebruik voor FAT-bestandssystemen.",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Bestanden worden verplaatst naar de .stversions-map wanneer ze vervangen of verwijderd zijn door Syncthing.",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Verwijderde of vervangen bestanden worden verplaatst naar de map .stversions.",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Bestanden worden verplaatst naar de .stversions-map wanneer ze vervangen of verwijderd zijn door Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Bestanden worden verplaatst naar versies met tijdsaanduiding in een .stversions-map wanneer ze vervangen of verwijderd zijn door Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Bestanden worden niet door Syncthing vervangen of verwijderd, maar verplaatst naar de map .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.": "Bestanden zijn beschermt tegen aanpassingen die gemaakt zijn door andere apparaten, maar aanpassingen op dit apparaat worden doorgestuurd naar de rest van het cluster.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Bestanden worden verplaatst naar versies met tijdsaanduiding in een .stversions-map wanneer ze vervangen of verwijderd zijn door 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.": "Bestanden zijn beschermd tegen wijzigingen die op andere apparaten gemaakt zijn, maar wijzigingen die op dit apparaat gemaakt zijn, worden naar de rest van de cluster verzonden.",
|
||||
"Filesystem Notifications": "Meldingen van bestandssysteem",
|
||||
"Filesystem Watcher Errors": "Fouten van bestandssysteem-opvolger",
|
||||
"Filter by date": "Filteren op datum",
|
||||
@@ -118,57 +119,58 @@
|
||||
"Folder Path": "Maplocatie",
|
||||
"Folder Type": "Soort map",
|
||||
"Folders": "Mappen",
|
||||
"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.": "Voor de volgende mappen trad een fout op tijdens het beginnen opvolgen van wijzigingen. Er zal elke minuut opnieuw geprobeerd worden, dus de fouten kunnen snel verdwijnen. Als ze blijven voorkomen, probeer dan het onderliggend probleem op te lossen en vraag om hulp als u dat niet kunt.",
|
||||
"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.": "Voor de volgende mappen trad een fout op tijdens het beginnen opvolgen van wijzigingen. Er zal elke minuut opnieuw geprobeerd worden, dus de fouten kunnen snel verdwijnen. Als ze blijven voorkomen, probeer dan het onderliggende probleem op te lossen en vraag om hulp als u dat niet kunt.",
|
||||
"Full Rescan Interval (s)": "Interval voor volledig opnieuw scannen (s)",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "GUI-wachtwoord",
|
||||
"GUI Authentication User": "GUI-gebruikersnaam",
|
||||
"GUI Listen Address": "GUI-luisteradres",
|
||||
"GUI Listen Addresses": "GUI-adres",
|
||||
"GUI Listen Addresses": "GUI-luisteradressen",
|
||||
"GUI Theme": "GUI-thema",
|
||||
"General": "Algemeen",
|
||||
"Generate": "Genereer",
|
||||
"Generate": "Genereren",
|
||||
"Global Changes": "Algemene wijzigingen",
|
||||
"Global Discovery": "Globaal zoeken",
|
||||
"Global Discovery": "Globale ontdekking",
|
||||
"Global Discovery Servers": "Globale ontdekkingsservers",
|
||||
"Global State": "Globale status",
|
||||
"Help": "Help",
|
||||
"Home page": "Startpagina",
|
||||
"Ignore": "Negeer",
|
||||
"Ignore Patterns": "Bestanden negeren",
|
||||
"Ignore Permissions": "Negeer bestandspermissies",
|
||||
"Incoming Rate Limit (KiB/s)": "Limiteer downloadsnelheid (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Een verkeerde configuratie kan bestanden in mappen beschadigen en/of Syncthing onbruikbaar maken.",
|
||||
"Ignore": "Negeren",
|
||||
"Ignore Patterns": "Negeerpatronen",
|
||||
"Ignore Permissions": "Machtigingen negeren",
|
||||
"Incoming Rate Limit (KiB/s)": "Begrenzing downloadsnelheid (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Verkeerde configuratie kan de inhoud van uw map beschadigen en Syncthing onbruikbaar maken.",
|
||||
"Introduced By": "Geïntroduceerd door",
|
||||
"Introducer": "Introductie-apparaat",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversie van de gegeven voorwaarde (i.e. niet uitsluiten)",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversie van de gegeven voorwaarde (d.w.z. niet uitsluiten)",
|
||||
"Keep Versions": "Versies behouden",
|
||||
"Largest First": "Grootste eerst",
|
||||
"Last File Received": "Laatst ontvangen bestand",
|
||||
"Last Scan": "Laatste scan",
|
||||
"Last seen": "Laatst gezien op",
|
||||
"Later": "Later",
|
||||
"Latest Change": "Meest recente wijziging",
|
||||
"Latest Change": "Laatste wijziging",
|
||||
"Learn more": "Lees meer",
|
||||
"Listeners": "Luisteraars",
|
||||
"Loading data...": "Gegevens laden...",
|
||||
"Loading...": "Laden...",
|
||||
"Local Discovery": "Lokaal zoeken",
|
||||
"Local Discovery": "Lokale ontdekking",
|
||||
"Local State": "Lokale status",
|
||||
"Local State (Total)": "Lokale status (totaal)",
|
||||
"Log": "Logboek",
|
||||
"Log tailing paused. Click here to continue.": "Loggen gepauzeerd. Klik hier om verder te gaan.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Loggen gepauzeerd. Naar beneden scrollen om door te gaan.",
|
||||
"Logs": "Logboeken",
|
||||
"Major Upgrade": "Grote update",
|
||||
"Major Upgrade": "Grote upgrade",
|
||||
"Mass actions": "Groepsacties",
|
||||
"Master": "Master",
|
||||
"Maximum Age": "Maximum leeftijd",
|
||||
"Maximum Age": "Maximale leeftijd",
|
||||
"Metadata Only": "Alleen metadata",
|
||||
"Minimum Free Disk Space": "Minimale vrije schijfruimte",
|
||||
"Mod. Device": "Wijzigend apparaat",
|
||||
"Mod. Time": "Tijdstip van wijziging",
|
||||
"Move to top of queue": "Verplaats naar het begin van de wachtrij",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Wildcard op meerdere niveaus (toepasbaar op meerdere mapniveaus)",
|
||||
"Move to top of queue": "Naar begin van wachtrij verplaatsen",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Wildcard op meerdere niveaus (komt overeen met meerdere mapniveaus)",
|
||||
"Never": "Nooit",
|
||||
"New Device": "Nieuw apparaat",
|
||||
"New Folder": "Nieuwe map",
|
||||
@@ -179,177 +181,178 @@
|
||||
"No upgrades": "Geen upgrades",
|
||||
"Normal": "Normaal",
|
||||
"Notice": "Mededeling",
|
||||
"OK": "OK",
|
||||
"OK": "Ok",
|
||||
"Off": "Uit",
|
||||
"Oldest First": "Oudste eerst",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Optioneel label met een beschrijving voor de map. Kan verschillend zijn op elk apparaat.",
|
||||
"Options": "Opties",
|
||||
"Out of Sync": "Niet gesynchroniseerd",
|
||||
"Out of Sync Items": "Niet-gesynchroniseerde items",
|
||||
"Outgoing Rate Limit (KiB/s)": "Uitgaande snelheidslimiet (KiB/s)",
|
||||
"Override Changes": "Veranderingen overschrijven",
|
||||
"Outgoing Rate Limit (KiB/s)": "Begrenzing uploadsnelheid (KiB/s)",
|
||||
"Override Changes": "Wijzigingen overschrijven",
|
||||
"Path": "Pad",
|
||||
"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": "Locatie van de map op de lokale computer. Als deze niet bestaat zal de map aangemaakt worden. De tilde (~) kan gebruikt worden als snelkoppeling voor ",
|
||||
"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%}.": "Locatie waar nieuwe automatisch aanvaarde mappen aangemaakt zullen worden, evenals de standaard voorgestelde locatie bij toevoegen van nieuwe mappen via de gebruikersinterface. Een tilde (~) zet uit tot {{tilde}}.",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Pad waar de verschillende versies opgeslagen moeten worden (leeg laten voor de standaard .stversion-map in de gedeelde map).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Betandspad waar de versies opgeslagen moeten worden (leeg laten voor de standaard .stversions subfolder).",
|
||||
"Pause": "Pauze",
|
||||
"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": "Pad naar de map op de lokale computer. Zal aangemaakt worden als het niet bestaat. De tilde (~) kan gebruikt worden als snelkoppeling voor",
|
||||
"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%}.": "Pad waar nieuwe automatisch aanvaarde mappen aangemaakt zullen worden, evenals het standaard voorgestelde pad bij toevoegen van nieuwe mappen via de gebruikersinterface. Een tilde (~) zet uit tot {{tilde}}.",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Pad waar versies opgeslagen moeten worden (leeg laten voor de standaard .stversion-map in de gedeelde map).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Pad waar versies opgeslagen moeten worden (leeg laten voor de standaard .stversion-map in de map).",
|
||||
"Pause": "Pauzeren",
|
||||
"Pause All": "Alles pauzeren",
|
||||
"Paused": "Gepauseerd",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodiek scannen na opgegeven interval; opvolgen van wijzigingen uitgeschakeld",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodiek scannen na opgegeven interval; opvolgen van wijzigingen ingeschakeld",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodiek scannen na opgegeven interval; instellen van opvolgen van wijzigingen mislukt. Elke minuut wordt opnieuw geprobeerd:",
|
||||
"Paused": "Gepauzeerd",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodiek scannen met opgegeven interval; opvolgen van wijzigingen uitgeschakeld",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodiek scannen met opgegeven interval; opvolgen van wijzigingen ingeschakeld",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodiek scannen met opgegeven interval; instellen van opvolgen van wijzigingen mislukt. Om de minuut opnieuw proberen:",
|
||||
"Permissions": "Machtigingen",
|
||||
"Please consult the release notes before performing a major upgrade.": "Lees eerst de release notes voordat u een grote update uitvoert.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Stel een gebruikersnaam en wachtwoord in bij 'Instellingen'.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Lees eerst de release-opmerkingen voordat u een grote upgrade uitvoert.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Stel een gebruikersnaam en wachtwoord voor GUI-authenticatie in via het instellingen-venster.",
|
||||
"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",
|
||||
"Preview": "Preview",
|
||||
"Preview Usage Report": "Preview gebruiksstatistieken",
|
||||
"Preview": "Voorbeeld",
|
||||
"Preview Usage Report": "Voorbeeld van gebruiksrapport",
|
||||
"Quick guide to supported patterns": "Snelgids voor ondersteunde patronen",
|
||||
"RAM Utilization": "Geheugengebruik",
|
||||
"RAM Utilization": "RAM-gebruik",
|
||||
"Random": "Willekeurig",
|
||||
"Recent Changes": "Recente wijzigingen",
|
||||
"Reduced by ignore patterns": "Verminderd door negeerpatronen",
|
||||
"Release Notes": "Release notes",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Uitgavekandidaten bevatten de laatste nieuwe functionaliteit en oplossingen voor problemen. Ze lijken op de traditionele, tweemaal per week, Syncthing-uitgaven.",
|
||||
"Release Notes": "Release-opmerkingen",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Release candidates bevatten de laatste functies en oplossingen voor problemen. Ze lijken op de traditionele tweewekelijkse Syncthing-releases.",
|
||||
"Remote Devices": "Externe apparaten",
|
||||
"Remove": "Verwijderen",
|
||||
"Remove Device": "Apparaat verwijderen",
|
||||
"Remove Folder": "Map verwijderen",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "De identifier voor de map is verplicht. Dit moet hetzelfde zijn op alle apparaten in het cluster. ",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Verplichte identificatiecode voor de map. Moet hetzelfde zijn op alle apparaten in de cluster. \n",
|
||||
"Rescan": "Opnieuw scannen",
|
||||
"Rescan All": "Scan alles opnieuw",
|
||||
"Rescan Interval": "Scanfrequentie",
|
||||
"Rescan All": "Alles opnieuw scannen",
|
||||
"Rescan Interval": "Scan-interval",
|
||||
"Rescans": "Rescans",
|
||||
"Restart": "Herstart",
|
||||
"Restart Needed": "Herstart nodig",
|
||||
"Restarting": "Herstarten",
|
||||
"Restart": "Opnieuw starten",
|
||||
"Restart Needed": "Opnieuw starten is nodig",
|
||||
"Restarting": "Opnieuw starten",
|
||||
"Restore": "Herstellen",
|
||||
"Restore Versions": "Versies herstellen",
|
||||
"Resume": "Hervatten",
|
||||
"Resume All": "Alles hervatten",
|
||||
"Reused": "Hergebruikt",
|
||||
"Reused": "Opnieuw gebruikt",
|
||||
"Running": "Draaiend",
|
||||
"Save": "Bewaar",
|
||||
"Save": "Opslaan",
|
||||
"Scan Time Remaining": "Resterende scantijd",
|
||||
"Scanning": "Aan het zoeken",
|
||||
"Scanning": "Scannen",
|
||||
"See external versioner help for supported templated command line parameters.": "Bekijk de documentatie van de externe versiebeheerder voor ondersteunde sjabloon-opdrachtregelparameters.",
|
||||
"See external versioning help for supported templated command line parameters.": "Bekijk de documentatie van het externe versiebeheer voor ondersteunde sjabloon-opdrachtregelparameters.",
|
||||
"See external versioning help for supported templated command line parameters.": "Bekijk de documentatie van extern versiebeheer voor ondersteunde sjabloon-opdrachtregelparameters.",
|
||||
"Select a version": "Selecteer een versie",
|
||||
"Select latest version": "Meest recente versie selecteren",
|
||||
"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.",
|
||||
"Select the folders to share with this device.": "Selecteer de mappen om met dit apparaat te delen.",
|
||||
"Send & Receive": "Verzenden & ontvangen",
|
||||
"Select the folders to share with this device.": "Selecteer de mappen om te delen met dit apparaat.",
|
||||
"Send & Receive": "Verzenden en ontvangen",
|
||||
"Send Only": "Alleen verzenden",
|
||||
"Settings": "Instellingen",
|
||||
"Share": "Delen",
|
||||
"Share Folder": "Deel map",
|
||||
"Share Folders With Device": "Deel mappen met apparaat",
|
||||
"Share Folder": "Map dele",
|
||||
"Share Folders With Device": "Mappen delen met apparaat",
|
||||
"Share With Devices": "Delen met apparaten",
|
||||
"Share this folder?": "Deze map delen?",
|
||||
"Shared With": "Gedeeld met",
|
||||
"Show ID": "Toon ID",
|
||||
"Sharing": "Delen",
|
||||
"Show ID": "ID weergeven",
|
||||
"Show QR": "QR weergeven",
|
||||
"Show diff with previous version": "Verschil met vorige versie tonen",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wordt i.p.v. het apparaat-ID getoond in de clusterstatus. Deze naam wordt aan andere apparaten voorgesteld als optionele standaard naam.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wordt in plaats van het apparaat-ID in de cluster status getoond. Zal geupdate worden naar de naam die het apparaat uitzend. ",
|
||||
"Shutdown": "Sluit af",
|
||||
"Show diff with previous version": "Verschil met vorige versie weergeven",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Weergegeven in plaats van de apparaat-ID in de cluster-status. Zal aan andere apparaten voorgesteld worden als een optionele standaardnaam.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Weergegeven in plaats van de apparaat-ID in de clusterstatus. Zal bijgewerkt worden met de naam die het apparaat voorstelt wanneer leeg gelaten.",
|
||||
"Shutdown": "Afsluiten",
|
||||
"Shutdown Complete": "Afsluiten voltooid",
|
||||
"Simple File Versioning": "Eenvoudig versiebeheer",
|
||||
"Single level wildcard (matches within a directory only)": "Wildcard op enkelvoudig niveau (alleen toepasbaar binnen een map)",
|
||||
"Single level wildcard (matches within a directory only)": "Wildcard op enkelvoudig niveau (komt alleen overeen binnen een map)",
|
||||
"Size": "Grootte",
|
||||
"Smallest First": "Kleinste eerst",
|
||||
"Some items could not be restored:": "Sommige items konden niet hersteld worden.",
|
||||
"Some items could not be restored:": "Sommige items konden niet hersteld worden:",
|
||||
"Source Code": "Broncode",
|
||||
"Stable releases and release candidates": "Stabiele versies en uitgavekandidaten",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabiele uitgaven zijn met ongeveer twee weken vertraagd. Tijdens deze periode worden ze als uitgavekandidaten getest.",
|
||||
"Stable releases only": "Alleen stabiele versies",
|
||||
"Staggered File Versioning": "Gelaagd versiebeheer",
|
||||
"Start Browser": "Start browser",
|
||||
"Stable releases and release candidates": "Stabiele releases en release candidates",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabiele releases worden met ongeveer twee weken uitgesteld. Tijdens deze periode worden ze als release canditates getest.",
|
||||
"Stable releases only": "Alleen stabiele releases",
|
||||
"Staggered File Versioning": "Gespreid versiebeheer",
|
||||
"Start Browser": "Browser starten",
|
||||
"Statistics": "Statistieken",
|
||||
"Stopped": "Gestopt",
|
||||
"Support": "Support",
|
||||
"Sync Protocol Listen Addresses": "Synchronisatie protocol luister adres",
|
||||
"Syncing": "Aan het synchroniseren",
|
||||
"Syncthing has been shut down.": "Syncthing is afgesloten",
|
||||
"Syncthing includes the following software or portions thereof:": "De volgende software of delen daarvan zijn onderdeel van syncthing:",
|
||||
"Support": "Ondersteuning",
|
||||
"Sync Protocol Listen Addresses": "Luisteradressen synchronisatieprotocol",
|
||||
"Syncing": "Synchroniseren",
|
||||
"Syncthing has been shut down.": "Syncthing werd afgesloten.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing bevat de volgende software of delen daarvan:",
|
||||
"Syncthing is restarting.": "Syncthing is aan het herstarten.",
|
||||
"Syncthing is upgrading.": "Syncthing is aan het upgraden.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt afgesloten te zijn, of er is een verbindingsprobleem met het internet. Nieuwe poging....",
|
||||
"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 je verzoek. Refresh de pagina of herstart Syncthing als de problemen zich blijven voordoen. ",
|
||||
"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. ",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "De beheerdersinterface van Syncthing is ingesteld om externe toegang zonder wachtwoord toe te staan.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "De verzamelde statistieken zijn publiek beschikbaar op de onderstaande URL.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De configuratie is opslagen maar nog niet actief. Syncthing moet opnieuw opgestart worden om de nieuwe configuratie te activeren.",
|
||||
"The device ID cannot be blank.": "Het apparaat-ID mag niet leeg zijn.",
|
||||
"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).": "Het hier in te vullen apparaat-ID kan gevonden worden in het \"Acties > ID weergeven\"-venster op de andere apparaten. Spaties en gedachtestreepjes zijn optioneel (en worden genegeerd).",
|
||||
"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.": "Het versleutelde gebruiksrapport wordt dagelijks gestuurd en wordt gebruikt om de verschillende platformen, mappengrootte en versies op te volgen. Als de reeks gegevens wijzigt zal dit scherm opnieuw worden getoond.",
|
||||
"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.": "Dit apparaat-ID lijkt ongeldig. Het apparaat-ID bestaat uit 52 of 56 letters en cijfers, spaties en streepjes zijn optioneel.",
|
||||
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "De eerste parameter is het pad naar de map en de tweede parameter is het relatieve pad binnen die map.",
|
||||
"The folder ID cannot be blank.": "Het map-ID mag niet leeg zijn.",
|
||||
"The folder ID must be unique.": "Het map-ID moet uniek zijn.",
|
||||
"The folder path cannot be blank.": "De map locatie mag niet leeg zijn.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "De configuratie is opslagen maar nog niet ingeschakeld. Syncthing moet opnieuw gestart worden om de nieuwe configuratie in te schakelen.",
|
||||
"The device ID cannot be blank.": "De apparaat-ID mag niet leeg zijn.",
|
||||
"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).": "De hier in te vullen apparaat-ID kan gevonden worden in het venster \"Acties > ID weergeven\" op het andere apparaat. Spaties en streepjes zijn optioneel (en worden genegeerd).",
|
||||
"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.": "Het versleutelde gebruiksrapport wordt dagelijks verzonden. Het wordt gebruikt om veelgebruikte platformen, mapgroottes en app-versies te volgen. Als de gerapporteerde dataset gewijzigd wordt, zal dit dialoogvenster opnieuw weergegeven worden.",
|
||||
"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.": "De opgegeven apparaat-ID ziet er niet goed uit. Het moet een reeks van 52 of 56 tekens zijn, bestaande uit letters en cijfers, waarbij spaties en streepjes optioneel zijn.",
|
||||
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "De eerste opdrachtregelparameter is het pad naar de map en de tweede parameter is het relatieve pad binnen de map.",
|
||||
"The folder ID cannot be blank.": "De map-ID mag niet leeg zijn.",
|
||||
"The folder ID must be unique.": "De map-ID moet uniek zijn.",
|
||||
"The folder path cannot be blank.": "Het pad naar de map mag niet leeg zijn.",
|
||||
"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.": "De volgende intervallen worden gebruikt: het eerste uur worden versies iedere 30 seconden bewaard, de eerste dag worden versies ieder uur bewaard, de eerste 30 dagen worden versies iedere dag bewaard, tot de maximale leeftijd worden versies iedere week bewaard.",
|
||||
"The following items could not be synchronized.": "De volgende bestanden konden niet worden gesynchroniseerd.",
|
||||
"The maximum age must be a number and cannot be blank.": "De maximum leeftijd moet uit cijfers bestaan en mag niet leeggelaten worden.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "De maximale tijdsduur om een versie te bewaren (in dagen, gebruik 0 om versies voor altijd te bewaren).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Het percentage minimale vrije schijfruimte dient een positief nummer tussen 0 en 100 (inclusief) te zijn.",
|
||||
"The number of days must be a number and cannot be blank.": "Het aantal dagen moet een getal zijn en kan niet leeg gelaten worden.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Het aantal dagen dat bestanden in de prullenbak blijven. Type 0 voor oneindig.",
|
||||
"The number of old versions to keep, per file.": "Het aantal versies dat bewaard moet worden per file.",
|
||||
"The number of versions must be a number and cannot be blank.": "Het aantal nummers moet een getal zijn en mag niet leeg blijven.",
|
||||
"The path cannot be blank.": "Het bestandspad mag niet leeg zijn.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "De snelheidslimiet moet een positief nummer zijn (0: geen limiet)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "De scanfrequentie moet een positief getal in seconden zijn.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Het wordt automatisch opnieuw geprobeerd. Bestanden worden gesynchroniseerd als de fout is hersteld.",
|
||||
"The following items could not be synchronized.": "De volgende items konden niet gesynchroniseerd worden.",
|
||||
"The maximum age must be a number and cannot be blank.": "De maximumleeftijd moet een getal zijn en mag niet leeg zijn.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "De maximale tijd om een versie te bewaren (in dagen, instellen op 0 om versies voor altijd te bewaren).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Het percentage minimale vrije schijfruimte moet een positief getal tussen 0 en 100 zijn.",
|
||||
"The number of days must be a number and cannot be blank.": "Het aantal dagen moet een getal zijn en mag niet leeg zijn.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Het aantal dagen om bestanden in de prullenbak te bewaren. Nul betekent voor altijd.",
|
||||
"The number of old versions to keep, per file.": "Het aantal te bewaren oude versies, per bestand.",
|
||||
"The number of versions must be a number and cannot be blank.": "Het aantal versies moet een getal zijn en mag niet leeg ziijn.",
|
||||
"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.",
|
||||
"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 kwaadwilligen eenvoudig toegang geven tot het lezen en wijzigen van bestanden op uw computer.",
|
||||
"This is a major version upgrade.": "Dit is een grote update.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Deze instelling beheert de benodigde vrije ruimte op de home (index database) schijf.",
|
||||
"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.",
|
||||
"This is a major version upgrade.": "Dit is een grote versie-upgrade.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Deze instelling bepaalt de benodigde vrije ruimte op de home-schijf (d.w.z. de indexdatabase).",
|
||||
"Time": "Tijd",
|
||||
"Time the item was last modified": "Tijdstip waarop het item laatst gewijzigd is",
|
||||
"Trash Can File Versioning": "Versiebeheer bestanden prullenbak",
|
||||
"Trash Can File Versioning": "Versiebeheer prullenbak",
|
||||
"Type": "Type",
|
||||
"Unavailable": "Niet beschikbaar",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Niet beschikbaar of uitgeschakeld door administrator of beheerder",
|
||||
"Undecided (will prompt)": "Onbeslist (zal bevestiging vragen)",
|
||||
"Unknown": "Onbekend",
|
||||
"Unshared": "Niet gedeeld",
|
||||
"Unused": "Ongebruikt",
|
||||
"Up to Date": "Gesynchroniseerd",
|
||||
"Unused": "Niet gebruikt",
|
||||
"Up to Date": "Up-to-date",
|
||||
"Updated": "Bijgewerkt",
|
||||
"Upgrade": "Upgrade",
|
||||
"Upgrade To {%version%}": "Upgrade naar {{version}}",
|
||||
"Upgrading": "Bezig met upgrade",
|
||||
"Upgrade": "Upgraden",
|
||||
"Upgrade To {%version%}": "Upgraden naar {{version}}",
|
||||
"Upgrading": "Upgraden",
|
||||
"Upload Rate": "Uploadsnelheid",
|
||||
"Uptime": "Uptime",
|
||||
"Usage reporting is always enabled for candidate releases.": "Rapportage van gebruik is altijd ingeschakeld voor de kandidaat-uitgaven.",
|
||||
"Use HTTPS for GUI": "Gebruik HTTPS voor de GUI",
|
||||
"Uptime": "Bedrijfstijd",
|
||||
"Usage reporting is always enabled for candidate releases.": "Gebruiksrapportering is altijd ingeschakeld voor de kandidaat-releases.",
|
||||
"Use HTTPS for GUI": "HTTPS gebruiken voor GUI",
|
||||
"Version": "Versie",
|
||||
"Versions": "Versies",
|
||||
"Versions Path": "Bestandspad versies",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versies worden automatisch verwijderd als deze ouder zijn dan de maximale leeftijd of als ze het maximaal aantal toegestane bestanden per interval overschrijden. ",
|
||||
"Versions Path": "Pad voor versies",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Versies worden automatisch verwijderd als deze ouder zijn dan de maximale leeftijd of als ze het maximaal aantal toegestane bestanden in een interval overschrijden.",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Waarschuwing, dit pad is een bovenliggende map van een bestaande map \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Waarschuwing, dit pad is een bovenliggende map van een bestaande map \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Waarschuwing, dit bestandspad is een submap van een bestaande map \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Waarschuwing, dit ppad is een onderliggende map van een bestaande map \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Waarschuwing, dit pad is een onderliggende map van een bestaande map \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Waarschuwing: als u een externe opvolger gebruikt zoals {{syncthingInotify}}, moet u zich ervan verzekeren dat die uitgeschakeld is.",
|
||||
"Watch for Changes": "Wijzigingen opvolgen",
|
||||
"Watching for Changes": "Wijzigingen opvolgen",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Wanneer een nieuw toestel wordt toegevoegd, houd er dan rekening mee dat dit toestel ook aan de andere kant moet worden toegevoegd.",
|
||||
"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.": "Houd er bij het toevoegen van nieuwe mappen rekening mee dat het map-ID gebruikt wordt om mappen tussen apparaten te verbinden. Dit ID is hoofdlettergevoelig en moet identiek zijn op andere apparaten.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Houd er bij het toevoegen van een nieuw apparaat rekening mee dat dit apparaat ook aan de andere kant moet toegevoegd worden.",
|
||||
"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.": "Houd er bij het toevoegen van een nieuwe map rekening mee dat de map-ID gebruikt wordt om mappen aan elkaar te koppelen tussen apparaten. Ze zijn hoofdlettergevoelig en moeten exact overeenkomen op alle apparaten.",
|
||||
"Yes": "Ja",
|
||||
"You can also select one of these nearby devices:": "U kunt ook een van de apparaten die dichtbij zijn selecteren:",
|
||||
"You can change your choice at any time in the Settings dialog.": "U kunt uw keuze op elk moment aanpassen in de Instellingen.",
|
||||
"You can read more about the two release channels at the link below.": "U kunt meer te weten komen over de twee uitgavekanalen via onderstaande link.",
|
||||
"You must keep at least one version.": "Minstens 1 versie moet bewaard blijven.",
|
||||
"You can also select one of these nearby devices:": "U kunt ook een van deze apparaten in de buurt selecteren:",
|
||||
"You can change your choice at any time in the Settings dialog.": "U kunt uw keuze op elk moment aanpassen in het instellingen-venster.",
|
||||
"You can read more about the two release channels at the link below.": "U kunt meer te weten komen over de twee release-kanalen via onderstaande link.",
|
||||
"You must keep at least one version.": "U moet minstens één versie bewaren.",
|
||||
"days": "dagen",
|
||||
"directories": "Mappen",
|
||||
"files": "Bestanden",
|
||||
"directories": "mappen",
|
||||
"files": "bestanden",
|
||||
"full documentation": "volledige documentatie",
|
||||
"items": "objecten",
|
||||
"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."
|
||||
}
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID urządzenia",
|
||||
"Device Identification": "Identyfikator urządzenia",
|
||||
"Device Name": "Nazwa urządzenia",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Urządzenie, które jako ostatnie zmodyfikowało element",
|
||||
"Devices": "Urządzenia",
|
||||
"Disabled": "Wyłączone",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Status lokalny (suma)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Śledzenie loga wstrzymane. Kliknij tutaj aby wznowić",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logi",
|
||||
"Major Upgrade": "Ważna aktualizacja",
|
||||
"Mass actions": "Działania masowe",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Udostępnij dla urządzenia",
|
||||
"Share this folder?": "Udostępnić ten folder?",
|
||||
"Shared With": "Współdzielony z",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Pokaż ID",
|
||||
"Show QR": "Pokaż kod QR",
|
||||
"Show diff with previous version": "Pokaż różnice z poprzednią wersją",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID do dispositivo",
|
||||
"Device Identification": "Identificação do dispositivo",
|
||||
"Device Name": "Nome do dispositivo",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Dispositivo que modificou o item pela última vez",
|
||||
"Devices": "Dispositivos",
|
||||
"Disabled": "Desabilitado",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Estado local (total)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Observação do log pausada. Clique aqui para continuar.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Atualização \"major\"",
|
||||
"Mass actions": "Ações em massa",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Compartilhar com os dispositivos",
|
||||
"Share this folder?": "Compartilhar esta pasta?",
|
||||
"Shared With": "Compartilhada com",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR Code",
|
||||
"Show diff with previous version": "Mostrar diferenças da versão anterior",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID do dispositivo",
|
||||
"Device Identification": "Identificação do dispositivo",
|
||||
"Device Name": "Nome do dispositivo",
|
||||
"Device rate limits": "Limites de velocidade do dispositivo",
|
||||
"Device that last modified the item": "Último dispositivo a modificar o item",
|
||||
"Devices": "Dispositivos",
|
||||
"Disabled": "Desactivada",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Estado local (total)",
|
||||
"Log": "Registo",
|
||||
"Log tailing paused. Click here to continue.": "O acompanhamento do final do registo está em pausa. Clique aqui para continuar.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "O acompanhamento do final do registo está em pausa. Desloque para o final para continuar.",
|
||||
"Logs": "Registos",
|
||||
"Major Upgrade": "Actualização importante",
|
||||
"Mass actions": "Acções em massa",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Partilhar com os dispositivos",
|
||||
"Share this folder?": "Partilhar esta pasta?",
|
||||
"Shared With": "Partilhada com",
|
||||
"Sharing": "Partilha",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR",
|
||||
"Show diff with previous version": "Mostrar diferenças em relação à versão anterior",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID устройства",
|
||||
"Device Identification": "Идентификация устройства",
|
||||
"Device Name": "Имя устройства",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Устройство, последним изменившее объект",
|
||||
"Devices": "Устройства",
|
||||
"Disabled": "Отключено",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Локальное состояние (всего)",
|
||||
"Log": "Журнал",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Журналы",
|
||||
"Major Upgrade": "Обновление основной версии",
|
||||
"Mass actions": "Массовые действия",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Предоставить доступ устройствам",
|
||||
"Share this folder?": "Предоставить доступ к этой папке?",
|
||||
"Shared With": "Доступ предоставлен",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Показать ID",
|
||||
"Show QR": "Показать QR-код",
|
||||
"Show diff with previous version": "Показать различия с предыдущей версией",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID zariadenia",
|
||||
"Device Identification": "Identifikácia zariadenia",
|
||||
"Device Name": "Názov zariadenia",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Zariadenie, ktoré naposledy pozmenilo položku",
|
||||
"Devices": "Zariadenia",
|
||||
"Disabled": "Odpojené",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokálny status (celkový)",
|
||||
"Log": "Záznam",
|
||||
"Log tailing paused. Click here to continue.": "Posúvanie záznamu prerušené. Klikni pre pokračovanie.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Záznamy",
|
||||
"Major Upgrade": "Hlavná aktualizácia",
|
||||
"Mass actions": "Hromadná akcia",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Zdieľať so zariadeniami",
|
||||
"Share this folder?": "Zdieľať tento adresár?",
|
||||
"Shared With": "Zdieľané s",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Zobraziť ID",
|
||||
"Show QR": "Zobraziť QR",
|
||||
"Show diff with previous version": "Ukázať rozdiely s predchádzajúcou verziou",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "Enhetens ID",
|
||||
"Device Identification": "Enhetens identifikation",
|
||||
"Device Name": "Enhetsnamn",
|
||||
"Device rate limits": "Enhetshastighetsgränser",
|
||||
"Device that last modified the item": "Enhet som senast ändrade objektet",
|
||||
"Devices": "Enheter",
|
||||
"Disabled": "Inaktiverad",
|
||||
@@ -109,7 +110,7 @@
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Filer flyttas till datummärkta versioner i en .stversions mapp när de ersätts eller tas bort 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 skyddas från ändringar gjorda på andra enheter, men ändringar som görs på den här noden skickas till de andra klustermedlemmarna.",
|
||||
"Filesystem Notifications": "filsystemsnotifieringar",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filesystem Watcher Errors": "Filsystem bevakarfel",
|
||||
"Filter by date": "Filtrera efter datum",
|
||||
"Filter by name": "Filtrera efter namn",
|
||||
"Folder": "Mapp",
|
||||
@@ -118,7 +119,7 @@
|
||||
"Folder Path": "Mappsökväg",
|
||||
"Folder Type": "Mapptyp",
|
||||
"Folders": "Mappar",
|
||||
"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.": "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.",
|
||||
"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.": "För följande mappar uppstod ett fel när du började bevaka ändringar. Det kommer att omförsökas varje minut, så felen kan försvinna snart. Om de fortsätter, försök att åtgärda det underliggande problemet och fråga om hjälp om du inte kan.",
|
||||
"Full Rescan Interval (s)": "Fullständig omskanningsintervall(er)",
|
||||
"GUI": "Grafiskt gränssnitt",
|
||||
"GUI Authentication Password": "Gränssnittets autentiseringslösenord",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Lokalt tillstånd (totalt)",
|
||||
"Log": "Logg",
|
||||
"Log tailing paused. Click here to continue.": "Loggning pausad. Klicka här för att fortsätta.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Loggar",
|
||||
"Major Upgrade": "Större uppgradering",
|
||||
"Mass actions": "Massåtgärder",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Dela med enheter",
|
||||
"Share this folder?": "Dela denna mapp?",
|
||||
"Shared With": "Delad med",
|
||||
"Sharing": "Delning",
|
||||
"Show ID": "Visa ID",
|
||||
"Show QR": "Visa QR",
|
||||
"Show diff with previous version": "Visa skillnad med tidigare version",
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
{
|
||||
"A device with that ID is already added.": "Bu ID'yi taşıyan aygıt zaten eklendi.",
|
||||
"A negative number of days doesn't make sense.": "Eksi gün sayısı mantıklı bir ifade değil.",
|
||||
"A new major version may not be compatible with previous versions.": "Yeni ana sürüm, önceki sürümlerle uyumlu olmayabilir.",
|
||||
"API Key": "API Anahtarı",
|
||||
"About": "Hakkında",
|
||||
"Action": "Eylem",
|
||||
"Actions": "Eylemler",
|
||||
"Add": "Ekle",
|
||||
"Add Device": "Aygıt Ekle",
|
||||
"Add Folder": "Klasör Ekle",
|
||||
"Add Remote Device": "Uzak Aygıt Ekle",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Add devices from the introducer to our device list, for mutually shared folders.",
|
||||
"Add new folder?": "Yeni klasör ekle?",
|
||||
"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.": "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.",
|
||||
"Address": "Adres",
|
||||
"Addresses": "Adresler",
|
||||
"Advanced": "Gelişmiş Düzey",
|
||||
"Advanced Configuration": "Gelişmiş Yapılandırma",
|
||||
"Advanced settings": "Gelişmiş ayarlar",
|
||||
"All Data": "Tüm Veriler",
|
||||
"Allow Anonymous Usage Reporting?": "Anonim kullanımın raporlanmasına izin veriyor musun ?",
|
||||
"Allowed Networks": "Allowed Networks",
|
||||
"Alphabetic": "Alfabetik",
|
||||
"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.",
|
||||
"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 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.": "Sürümleme işlemini harici bir komut yürütüyor. Dosyayı eşzamanlama klasöründen kaldırmak zorunda.",
|
||||
"Anonymous Usage Reporting": "Anonim Kullanım Raporlaması",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "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.": "Tanıtıcı bir cihaz üzerinde yapılandırılan cihazlar bu cihaza da eklenecektir.",
|
||||
"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?",
|
||||
"Auto Accept": "Auto Accept",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Kendiliğinden yükseltme artık kararlı dağıtımlar ve sürüm adayları arasında seçim yapmayı sağlıyor.",
|
||||
"Automatic upgrades": "Kendiliğinden yükseltmeler",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatically create or share folders that this device advertises at the default path.",
|
||||
"Available debug logging facilities:": "Available debug logging facilities:",
|
||||
"Be careful!": "Dikkatli ol!",
|
||||
"Bugs": "Yazılım Hataları",
|
||||
"CPU Utilization": "İşlemci Kullanımı",
|
||||
"Changelog": "Değişim Günlüğü",
|
||||
"Clean out after": "Şundan sonra temizle",
|
||||
"Click to see discovery failures": "Click to see discovery failures",
|
||||
"Close": "Kapat",
|
||||
"Command": "Komut",
|
||||
"Comment, when used at the start of a line": "Satır başında kullanıldığında açıklama özelliği taşır",
|
||||
"Compression": "Sıkıştırma",
|
||||
"Configured": "Yapılandırıldı",
|
||||
"Connection Error": "Bağlantı hatası",
|
||||
"Connection Type": "Bağlantı Türü",
|
||||
"Connections": "Connections",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "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.",
|
||||
"Copied from elsewhere": "Başka bir yerden kopyalanmış",
|
||||
"Copied from original": "Aslından kopyalanmış",
|
||||
"Copyright © 2014-2016 the following Contributors:": "Telif Hakkı © 2014-2016 takip eden Katkıcılar:",
|
||||
"Copyright © 2014-2017 the following Contributors:": "Telif Hakkı © 2014-2017 takip eden Katkıcılar:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
|
||||
"Danger!": "Tehlike!",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default Folder Path": "Default Folder Path",
|
||||
"Deleted": "Silindi",
|
||||
"Device": "Aygıt",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" aygıtı ({{address}} adresindeki {{device}}) bağlanmak istiyor. Yeni aygıtı ekliyor musun?",
|
||||
"Device ID": "Aygıt ID",
|
||||
"Device Identification": "Aygıt Kimliği",
|
||||
"Device Name": "Aygıt Adı",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
"Devices": "Aygıtlar",
|
||||
"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",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:",
|
||||
"Disconnected": "Bağlantı Kesik",
|
||||
"Discovered": "Keşfedildi",
|
||||
"Discovery": "Keşif",
|
||||
"Discovery Failures": "Discovery Failures",
|
||||
"Do not restore": "Do not restore",
|
||||
"Do not restore all": "Do not restore all",
|
||||
"Do you want to enable watching for changes for all your folders?": "Do you want to enable watching for changes for all your folders?",
|
||||
"Documentation": "Belgeleme",
|
||||
"Download Rate": "İndirme Hızı",
|
||||
"Downloaded": "İndirilmiş",
|
||||
"Downloading": "İndiriliyor",
|
||||
"Edit": "Düzenle",
|
||||
"Edit Device": "Aygıtı Düzenle",
|
||||
"Edit Folder": "Klasörü Düzenle",
|
||||
"Editing": "Düzenleniyor",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Enable NAT traversal": "NAT çaprazlamasına izin ver",
|
||||
"Enable Relaying": "Enable Relaying",
|
||||
"Enabled": "Enabled",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Adreslerin kendiliğinden keşfedilebilmesi için ya adresleri virgülle ayırarak (\"tcp://ip:port\", \"tcp://host:port\") girin, ya da \"dynamic\" sözcüğünü girin.",
|
||||
"Enter ignore patterns, one per line.": "Gözardı edilmesini istediğiniz kalıp dizilerini her satıra bir tane olacak şekilde girin.",
|
||||
"Error": "Hata",
|
||||
"External File Versioning": "Harici Dosya Sürümleme İşlemi",
|
||||
"Failed Items": "Başarısız Olunan Ögeler",
|
||||
"Failed to load ignore patterns": "Failed to load ignore patterns",
|
||||
"Failed to setup, retrying": "Failed to setup, retrying",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
|
||||
"File Pull Order": "Dosya Çekme Sırası",
|
||||
"File Versioning": "Dosya Sürümleme İşlemi",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Değişimleri tararken dosya izin bilgilerini gözardı et. FAT dosya sistemlerinde kullanın.",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Files are moved to .stversions directory when replaced or deleted by Syncthing.",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Dosyalar Syncthing tarafından yeri değiştirildiğinde ya da silindiğinde .stversions klasörüne taşınır.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Dosyalar Syncthing tarafından yeri değiştirildiğinde ya da silindiğinde, tarih damgalı sürümleri .stversions klasörüne taşınır.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Dosyalar diğer aygıtlarda yapılan değişikliklerden korunur, ancak bu aygıttaki değişiklikler kümedeki diğer aygıtlara gönderilir.",
|
||||
"Filesystem Notifications": "Filesystem Notifications",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filter by date": "Filter by date",
|
||||
"Filter by name": "Filter by name",
|
||||
"Folder": "Klasör",
|
||||
"Folder ID": "Klasör ID",
|
||||
"Folder Label": "Klasör Etiketi",
|
||||
"Folder Path": "Klasör Yolu",
|
||||
"Folder Type": "Klasör Türü",
|
||||
"Folders": "Klasörler",
|
||||
"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.": "For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.",
|
||||
"Full Rescan Interval (s)": "Full Rescan Interval (s)",
|
||||
"GUI": "GUI / Grafiksel Kullanıcı Arayüzü",
|
||||
"GUI Authentication Password": "GUI Kimlik Doğrulaması için Kullanıcı Parolası",
|
||||
"GUI Authentication User": "GUI Kimlik Doğrulaması için Kullanıcı Adı",
|
||||
"GUI Listen Address": "GUI Listen Address",
|
||||
"GUI Listen Addresses": "GUI Dinleme/Bağlantı Adresleri",
|
||||
"GUI Theme": "GUI Gövdesi",
|
||||
"General": "General",
|
||||
"Generate": "Oluştur",
|
||||
"Global Changes": "Küresel Değişiklikler",
|
||||
"Global Discovery": "Küresel Discovery",
|
||||
"Global Discovery Servers": "Küresel Keşif Sunucuları",
|
||||
"Global State": "Küresel Durum",
|
||||
"Help": "Yardım",
|
||||
"Home page": "Ana sayfa",
|
||||
"Ignore": "Yoksay",
|
||||
"Ignore Patterns": "Gözardı Edilen Kalıp Dizileri",
|
||||
"Ignore Permissions": "İzinleri Gözardı Et",
|
||||
"Incoming Rate Limit (KiB/s)": "İndirme Oranı Sınırı (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Yanlış yapılandırma klasör içeriğine zarar verebilir ve Syncthing'i çalışamaz hale getirebilir.",
|
||||
"Introduced By": "Tanıtan",
|
||||
"Introducer": "Tanıtıcı",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Verilen koşulun ters çevirilmesi (örneğin: yok sayma)",
|
||||
"Keep Versions": "Sürümleri Tut",
|
||||
"Largest First": "En büyük olan önce",
|
||||
"Last File Received": "Alınan Son Dosya",
|
||||
"Last Scan": "Son Tarama",
|
||||
"Last seen": "Son görülme",
|
||||
"Later": "Sonra",
|
||||
"Latest Change": "Son Değişim",
|
||||
"Learn more": "Daha çoğunu öğren",
|
||||
"Listeners": "Dinleyiciler",
|
||||
"Loading data...": "Loading data...",
|
||||
"Loading...": "Loading...",
|
||||
"Local Discovery": "Yerel Discovery",
|
||||
"Local State": "Yerel Durum",
|
||||
"Local State (Total)": "Yerel Durum (Toplamı)",
|
||||
"Log": "Log",
|
||||
"Log tailing paused. Click here to continue.": "Log tailing paused. Click here to continue.",
|
||||
"Logs": "Logs",
|
||||
"Major Upgrade": "Birincil Yükseltme",
|
||||
"Mass actions": "Mass actions",
|
||||
"Master": "Ana",
|
||||
"Maximum Age": "Azami Süre",
|
||||
"Metadata Only": "Yalnızca Üstveri",
|
||||
"Minimum Free Disk Space": "En Az Boş Disk Alanı",
|
||||
"Mod. Device": "Mod. Device",
|
||||
"Mod. Time": "Mod. Time",
|
||||
"Move to top of queue": "Kuyruğun başına taşı",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Çoklu düzey wildcard (çok sayıda dizin düzeyinde eşleşme)",
|
||||
"Never": "Asla",
|
||||
"New Device": "Yeni Aygıt",
|
||||
"New Folder": "Yeni Klasör",
|
||||
"Newest First": "En yeni olan önce",
|
||||
"No": "Hayır",
|
||||
"No File Versioning": "Dosya Sürümleme İşlemi Yok",
|
||||
"No files will be deleted as a result of this operation.": "No files will be deleted as a result of this operation.",
|
||||
"No upgrades": "Yükseltme yok",
|
||||
"Normal": "Olağan",
|
||||
"Notice": "Uyarı",
|
||||
"OK": "Tamam",
|
||||
"Off": "Kapalı",
|
||||
"Oldest First": "En eski olan önce",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Klasör için isteğe bağlı açıklayıcı etiket. Her aygıtta başka olabilir.",
|
||||
"Options": "Seçenekler",
|
||||
"Out of Sync": "Eşzamanlama Dışı",
|
||||
"Out of Sync Items": "Eşzamanlama Dışında Kalan Ögeler",
|
||||
"Outgoing Rate Limit (KiB/s)": "Yükleme hız sınırı (KB/sn)",
|
||||
"Override Changes": "Değişiklikleri Geçersiz kıl",
|
||||
"Path": "Yol",
|
||||
"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": "Yerel bilgisayardaki klasöre ulaşım yolu. Klasör yoksa yaratılacak. Tilde (~) karakterinin kısayol olarak kullanılabileceği yol",
|
||||
"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%}.": "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}}.",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Sürümlerin saklanması için gereken klasör yolu (öntanımlı .stversions klasörü için boş bırakın)",
|
||||
"Pause": "Duraklat",
|
||||
"Pause All": "Tümünü Duraklat",
|
||||
"Paused": "Duraklatıldı",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodic scanning at given interval and disabled watching for changes",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodic scanning at given interval and enabled watching for changes",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:",
|
||||
"Permissions": "Permissions",
|
||||
"Please consult the release notes before performing a major upgrade.": "Birincil bir yükseltme gerçekleştirmeden önce lütfen sürüm notlarını tetkik edin.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "GUI üzerinden Kimlik Doğrulaması yapmak için Ayarlar penceresinde Kullanıcı ve Parola tanımlayın lütfen.",
|
||||
"Please wait": "Lütfen Bekleyin",
|
||||
"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",
|
||||
"Preview": "Önizleme",
|
||||
"Preview Usage Report": "Kullanım raporunu gözden geçir",
|
||||
"Quick guide to supported patterns": "Desteklenen kalıp dizileri için hızlı rehber",
|
||||
"RAM Utilization": "RAM Kullanımı",
|
||||
"Random": "Rastgele",
|
||||
"Recent Changes": "Recent Changes",
|
||||
"Reduced by ignore patterns": "Gözardı edilen kalıp dizileri tarafından daraltıldı",
|
||||
"Release Notes": "Sürüm Notları",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Sürüm adayları en son özellikleri ve hata düzeltmelerini içerir. Bunlar, geleneksel olarak iki haftada bir yayımlanan Syncthing dağıtımlarına benzer.",
|
||||
"Remote Devices": "Uzak Aygıtlar",
|
||||
"Remove": "Kaldır",
|
||||
"Remove Device": "Remove Device",
|
||||
"Remove Folder": "Remove Folder",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Klasör için tanımlayıcı gereklidir. Tüm küme cihazlarda aynı olmalıdır.",
|
||||
"Rescan": "Tekrar Tara",
|
||||
"Rescan All": "Tümünü Tekrar Tara",
|
||||
"Rescan Interval": "Tarama Aralığı",
|
||||
"Rescans": "Rescans",
|
||||
"Restart": "Yeniden Başlat",
|
||||
"Restart Needed": "Yeniden başlatma gereklidir",
|
||||
"Restarting": "Yeniden başlatılıyor",
|
||||
"Restore": "Restore",
|
||||
"Restore Versions": "Restore Versions",
|
||||
"Resume": "Devam Et/Sürdür",
|
||||
"Resume All": "Tümünü Devam Ettir",
|
||||
"Reused": "Yeniden Kullanılan",
|
||||
"Running": "Running",
|
||||
"Save": "Kaydet",
|
||||
"Scan Time Remaining": "Kalan Tarama Zamanı",
|
||||
"Scanning": "Taranıyor",
|
||||
"See external versioner help for supported templated command line parameters.": "See external versioner help for supported templated command line parameters.",
|
||||
"See external versioning help for supported templated command line parameters.": "See external versioning help for supported templated command line parameters.",
|
||||
"Select a version": "Select a version",
|
||||
"Select latest version": "Select latest version",
|
||||
"Select oldest version": "Select oldest version",
|
||||
"Select the devices to share this folder with.": "Bu klasörü paylaşacağın aygıtları seç.",
|
||||
"Select the folders to share with this device.": "Bu aygıtla paylaşılacak klasörleri seç.",
|
||||
"Send & Receive": "Gönder & Al",
|
||||
"Send Only": "Yalnızca Gönder",
|
||||
"Settings": "Ayarlar",
|
||||
"Share": "Paylaş",
|
||||
"Share Folder": "Paylaşım Klasörü",
|
||||
"Share Folders With Device": "Klasörleri Aygıtla Paylaş",
|
||||
"Share With Devices": "Aygıtlar İle Paylaş",
|
||||
"Share this folder?": "Bu klasörü paylaş?",
|
||||
"Shared With": "Paylaşılan düğümler",
|
||||
"Show ID": "ID Göster",
|
||||
"Show QR": "QR Göster",
|
||||
"Show diff with previous version": "Show diff with previous version",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Küme durumunda Aygıt ID yerine bunu göster. Varsayılan ad isteğe bağlı olarak diğer aygıtlara ilan edilecektir.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Küme durumunda Aygıt ID yerine bunu göster. Eğer düğüm adı boş bırakılırsa düğüm adı güncellenip ilan edilecektir.",
|
||||
"Shutdown": "Kapat",
|
||||
"Shutdown Complete": "Kapatma İşlemi Tamamlandı",
|
||||
"Simple File Versioning": "Basit Dosya Sürümleme İşlemi",
|
||||
"Single level wildcard (matches within a directory only)": "Tekli düzey wildcard (yalnızca bir dizin içinde eşleşme)",
|
||||
"Size": "Size",
|
||||
"Smallest First": "En küçük olan önce",
|
||||
"Some items could not be restored:": "Some items could not be restored:",
|
||||
"Source Code": "Kaynak Kodu",
|
||||
"Stable releases and release candidates": "Kararlı dağıtımlar ve sürüm adayları",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Kararlı dağıtımlar aşağı yukarı iki hafta gecikir. Bu zaman zarfında sürüm adayı olarak sınanırlar.",
|
||||
"Stable releases only": "Yalnızca kararlı dağıtımlar",
|
||||
"Staggered File Versioning": "Aşamalı Dosya Sürümleme İşlemi",
|
||||
"Start Browser": "Tarayıcıyı Başlat",
|
||||
"Statistics": "İstatistikler",
|
||||
"Stopped": "Durduruldu",
|
||||
"Support": "Destek",
|
||||
"Sync Protocol Listen Addresses": "Sync Protokol Dinleme Adresleri",
|
||||
"Syncing": "Eşzamanlama gerçekleştiriliyor",
|
||||
"Syncthing has been shut down.": "Syncthing kapatıldı",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing aşağıdaki yazılımları veya bunların bölümlerini içermektedir:",
|
||||
"Syncthing is restarting.": "Syncthing yeniden başlatılıyor.",
|
||||
"Syncthing is upgrading.": "Syncthing yükseltiliyor.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing uygulaması çökmüş olabilir, veya internet bağlantınızda bir sorun var. Tekrar deniyor....",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing isteminizi işleme alırken bir sorunla karşılaştı. Lütfen sayfanızı yenileyin veya sorun devam ediyorsa Syncthing'i yeniden başlatın.",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthing yönetici arayüzü parolasız olarak uzaktan erişime izin verilecek şekilde yapılandırıldı.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Toplanan istatistikler halka açık biçimde aşağıdaki adrestedir.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Yapılandırma kaydedildi ancak etkinleştirilmedi. Etkinleştirmek için Syncthing yeniden başlatılmalı.",
|
||||
"The device ID cannot be blank.": "Aygıt ID değeri boş bırakılamaz.",
|
||||
"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).": "Buraya girilecek olan aygıt ID'si diğer aygıtlarda \"Eylemler > ID Göster\" penceresinde bulunabilir. Boşluklar ve çizgiler isteğe bağlıdır (gözardı edilmiş).",
|
||||
"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.": "Şifrelenmiş kullanım bilgisi günlük olarak gönderilir. Platform, klasör büyüklüğü ve uygulama sürümü hakkında bilgi toplanır. Toplanan bilgi türü değişecek olursa, sizden yeniden onay istenecek.",
|
||||
"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.": "Girilen aygıt ID'si geçerli gibi gözükmüyor. 52 ya da 56 karakter uzunluğunda, harf ve rakamlardan oluşmalı. Boşlukların ve kısa çizgilerin olup olmaması önemli değildir.",
|
||||
"The first command line parameter is the folder path and the second parameter is the relative path in the folder.": "İlk komut satırı parametresi klasör yoludur; ikinci parametre ise klasördeki göreceli yoldur. ",
|
||||
"The folder ID cannot be blank.": "Klasör ID değeri boş bırakılamaz",
|
||||
"The folder ID must be unique.": "Klasör ID benzersiz olmalıdır.",
|
||||
"The folder path cannot be blank.": "Klasör yolu boş bırakılamaz.",
|
||||
"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.": "Kullanılan zaman aralıkları: ilk bir saat zarfında her 30 saniyede bir, ilk gün zarfında saatte bir, ilk 30 gün zarfında her gün, azami süreye kadar geçen zamanda ise her hafta yeni bir sürüm değeri oluşturulur/tutulur.",
|
||||
"The following items could not be synchronized.": "Aşağıdaki ögelerin eşzamanlama işlemi gerçekleştirilemedi.",
|
||||
"The maximum age must be a number and cannot be blank.": "Azami süre tanımı boş bırakılmamalı ve bir sayı olarak tanımlanmalıdır.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Bir sürümün tutulması için belirlenen azami süre (sürümleri sürekli olarak tutabilmek için 0 değeri atayın)",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "En küçük boş disk alanı yüzde olarak 0 ve 100 (dahil) arasında kalan pozitif bir sayıyla tanımlanmalıdır.",
|
||||
"The number of days must be a number and cannot be blank.": "Gün sayısı boş bırakılmamalı ve bir sayı olarak tanımlanmalıdır.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Dosyaları çöp kutusunda tutma süresini tanımlayan gün sayısı. Sıfır devamlı/sürekli anlamına gelir.",
|
||||
"The number of old versions to keep, per file.": "Dosya başına saklanacak/tutulacak eski sürüm sayısı.",
|
||||
"The number of versions must be a number and cannot be blank.": "Sürümlerin sayısı boş bırakılamamalı ve bir sayı olarak tanımlanmalıdır.",
|
||||
"The path cannot be blank.": "Yol tanımı boş bırakılamaz.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Hız sınırı pozitif bir sayı olmalıdır. (0: sınırsız)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Tarama zaman aralığı, saniye cinsinden negatif olmayan bir sayı olmalıdır.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Kendiliğinden yeniden deneniyor; hata giderildiğinde eşzamanlama gerçekleştirilecek.",
|
||||
"This Device": "Bu Aygıt",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Hacker'ların bilgisayarındaki dosyaları okuma ve değiştirme yetkisine kolayca erişmelerini sağlayabilir.",
|
||||
"This is a major version upgrade.": "Ana sürüm yükseltmesidir.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
|
||||
"Time": "Zaman",
|
||||
"Time the item was last modified": "Time the item was last modified",
|
||||
"Trash Can File Versioning": "Çöp Kutusu Dosya Sürümleme İşlemi",
|
||||
"Type": "Tür",
|
||||
"Unavailable": "Unavailable",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Unavailable/Disabled by administrator or maintainer",
|
||||
"Undecided (will prompt)": "Undecided (will prompt)",
|
||||
"Unknown": "Bilinmiyor",
|
||||
"Unshared": "Paylaşılmayan",
|
||||
"Unused": "Kullanılmayan",
|
||||
"Up to Date": "Güncel",
|
||||
"Updated": "Güncellendi",
|
||||
"Upgrade": "Sürüm Yükseltme",
|
||||
"Upgrade To {%version%}": "{{version}} sürümüne yükselt",
|
||||
"Upgrading": "Yükseltiliyor",
|
||||
"Upload Rate": "Yükleme hızı",
|
||||
"Uptime": "Çalışma Zamanı",
|
||||
"Usage reporting is always enabled for candidate releases.": "Kullanım raporu sürüm adayları için her zaman etkindir.",
|
||||
"Use HTTPS for GUI": "GUI için HTTPS kullan",
|
||||
"Version": "Sürüm",
|
||||
"Versions": "Versions",
|
||||
"Versions Path": "Sürümler için Klasör Yolu",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Sürümler, tanımlı azami süre veya belirlenen zaman aralığı için izin verilen dosya sayısı aşılmışsa kendiliğinden silinir.",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Uyarı ! Bu yol varolan \"{{otherFolder}}\" klasörünün üst dizinidir.",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Uyarı ! Bu yol varolan \"{{otherFolderLabel}}\" ({{otherFolder}}) klasörünün üst dizinidir.",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Uyarı ! Bu yol varolan \"{{otherFolder}}\" klasörünün alt dizinidir.",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Uyarı ! Bu yol var olan \"{{otherFolderLabel}}\" ({{otherFolder}}) klasörünün alt dizinidir.",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warning: If you are using an external watcher like {{syncthingInotify}}, you should make sure it is deactivated.",
|
||||
"Watch for Changes": "Watch for Changes",
|
||||
"Watching for Changes": "Watching for Changes",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Yeni bir aygıt eklendiğinde, bu aygıtın karşı tarafa da eklenmesi gerektiğini unutmayın.",
|
||||
"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.": "Yeni bir klasör eklendiğinde, Klasör ID'nin klasörleri aygıtlar arasında bağlantılandırmak için kullanıldığını unutmayın. Klasör ID'ler büyük - küçük harf duyarlıdır ve tüm aygıtlarda tamı tamına eşleşmelidir.",
|
||||
"Yes": "Evet",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Seçiminizi herhangi bir zamanda Ayarlar penceresinde değiştirebilirsiniz.",
|
||||
"You can read more about the two release channels at the link below.": "İki dağıtım kanalıyla ilgili daha çoğunu aşağıdaki bağlantıdan okuyabilirsiniz.",
|
||||
"You must keep at least one version.": "En az bir sürümü tutmalısınız.",
|
||||
"days": "gün",
|
||||
"directories": "dizinler",
|
||||
"files": "dosyalar",
|
||||
"full documentation": "belgelendirme içeriğinin tümü",
|
||||
"items": "öğeler",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} \"{{folder}}\" klasörünü paylaşmak istiyor.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} \"{{folderlabel}}\" ({{folder}}) klasörünü paylaşmak istiyor."
|
||||
}
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "ID пристрою",
|
||||
"Device Identification": "Ідентифікатор пристрою",
|
||||
"Device Name": "Назва пристрою",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Пристрій, що останнім змінив елемент",
|
||||
"Devices": "Пристрої",
|
||||
"Disabled": "Вимкнено",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "Локальний статус (загалом)",
|
||||
"Log": "Журнал",
|
||||
"Log tailing paused. Click here to continue.": "Перемотка журналу призупинена. Натиснути для продовження.",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "Журнали",
|
||||
"Major Upgrade": "Мажорне оновлення",
|
||||
"Mass actions": "Масові операції",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "Розповсюдити між пристроями",
|
||||
"Share this folder?": "Розповсюдити цей каталог?",
|
||||
"Shared With": "Доступно для",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "Показати ID",
|
||||
"Show QR": "Показати QR-код",
|
||||
"Show diff with previous version": "Показати відмінності від попередньої версії",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "设备 ID",
|
||||
"Device Identification": "设备标识",
|
||||
"Device Name": "设备名",
|
||||
"Device rate limits": "设备速率限制",
|
||||
"Device that last modified the item": "最近修改该项的设备",
|
||||
"Devices": "设备",
|
||||
"Disabled": "已禁用",
|
||||
@@ -109,7 +110,7 @@
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "当某个文件在其他设备被替换或删除时,本设备将会在 .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.": "在其它设备中对该文件夹内文件的修改并不会被同步到本机,但是在本机上对其的修改,则会被同步到其它设备中。",
|
||||
"Filesystem Notifications": "文件系统通知",
|
||||
"Filesystem Watcher Errors": "Filesystem Watcher Errors",
|
||||
"Filesystem Watcher Errors": "文件系统监视器错误",
|
||||
"Filter by date": "按日期筛选",
|
||||
"Filter by name": "按名称筛选",
|
||||
"Folder": "文件夹",
|
||||
@@ -118,7 +119,7 @@
|
||||
"Folder Path": "文件夹路径",
|
||||
"Folder Type": "文件夹类型",
|
||||
"Folders": "文件夹",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "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.",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "开始监视下列文件夹时发生错误。由于每分钟都会重试,所以错误可能很快就消失。如果它们仍存在,请试着修复潜在问题,如不会则请求帮助。",
|
||||
"Full Rescan Interval (s)": "完整扫描间隔",
|
||||
"GUI": "图形用户界面",
|
||||
"GUI Authentication Password": "图形管理界面密码",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "本地状态汇总",
|
||||
"Log": "日志",
|
||||
"Log tailing paused. Click here to continue.": "已暂停日志跟踪。点击此处以继续。",
|
||||
"Log tailing paused. Scroll to bottom continue.": "已暂停日志跟踪。滚动到底部以继续。",
|
||||
"Logs": "日志",
|
||||
"Major Upgrade": "重大更新",
|
||||
"Mass actions": "批量操作",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "共享给",
|
||||
"Share this folder?": "是否共享该文件夹?",
|
||||
"Shared With": "共享给",
|
||||
"Sharing": "共享",
|
||||
"Show ID": "显示 ID",
|
||||
"Show QR": "显示 QR 码",
|
||||
"Show diff with previous version": "显示与先前版本的差异",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"Device ID": "裝置識別碼",
|
||||
"Device Identification": "裝置識別",
|
||||
"Device Name": "裝置名稱",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "前次修改裝置",
|
||||
"Devices": "裝置",
|
||||
"Disabled": "停用",
|
||||
@@ -158,6 +159,7 @@
|
||||
"Local State (Total)": "本機狀態 (總結)",
|
||||
"Log": "日誌",
|
||||
"Log tailing paused. Click here to continue.": "跟隨日誌暫停。點選這裡以繼續。",
|
||||
"Log tailing paused. Scroll to bottom continue.": "Log tailing paused. Scroll to bottom continue.",
|
||||
"Logs": "日誌",
|
||||
"Major Upgrade": "重大更新",
|
||||
"Mass actions": "大量操作",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Share With Devices": "與這些裝置共享",
|
||||
"Share this folder?": "分享此資料夾?",
|
||||
"Shared With": "與誰共享",
|
||||
"Sharing": "Sharing",
|
||||
"Show ID": "顯示識別碼",
|
||||
"Show QR": "顯示 QR 碼",
|
||||
"Show diff with previous version": "顯示與前一個版本的差異",
|
||||
|
||||
@@ -1 +1 @@
|
||||
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fr":"French","fr-CA":"French (Canada)","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","nl-BE":"Dutch (Belgium)","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-TW":"Chinese (Taiwan)"}
|
||||
var langPrettyprint = {"bg":"Bulgarian","ca@valencia":"Catalan (Valencian)","cs":"Czech","da":"Danish","de":"German","el":"Greek","en":"English","en-GB":"English (United Kingdom)","eo":"Esperanto","es":"Spanish","es-ES":"Spanish (Spain)","eu":"Basque","fr":"French","fr-CA":"French (Canada)","fy":"Western Frisian","hu":"Hungarian","it":"Italian","ja":"Japanese","ko-KR":"Korean (Korea)","lt":"Lithuanian","nb":"Norwegian Bokmål","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ru":"Russian","sk":"Slovak","sv":"Swedish","uk":"Ukrainian","zh-CN":"Chinese (China)","zh-TW":"Chinese (Taiwan)"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-GB","eo","es","es-ES","eu","fr","fr-CA","fy","hu","it","ja","ko-KR","lt","nb","nl","nl-BE","pl","pt-BR","pt-PT","ru","sk","sv","tr","uk","zh-CN","zh-TW"]
|
||||
var validLangs = ["bg","ca@valencia","cs","da","de","el","en","en-GB","eo","es","es-ES","eu","fr","fr-CA","fy","hu","it","ja","ko-KR","lt","nb","nl","pl","pt-BR","pt-PT","ru","sk","sv","uk","zh-CN","zh-TW"]
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
<table class="table table-condensed table-striped">
|
||||
<tbody>
|
||||
<tr ng-show="folder.label != undefined && folder.label.length > 0">
|
||||
<th><span class="fas fa-fw fa-folder-open"></span> <span translate>Folder ID</span></th>
|
||||
<th><span class="fas fa-fw fa-info-circle"></span> <span translate>Folder ID</span></th>
|
||||
<td class="text-right no-overflow-ellipse">{{folder.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -205,8 +205,8 @@ function unitPrefixed(input, binary) {
|
||||
if (input === undefined || isNaN(input)) {
|
||||
return '0 ';
|
||||
}
|
||||
factor = 1000;
|
||||
i = '';
|
||||
var factor = 1000;
|
||||
var i = '';
|
||||
if (binary) {
|
||||
factor = 1024;
|
||||
i = 'i'
|
||||
@@ -237,10 +237,14 @@ function unitPrefixed(input, binary) {
|
||||
}
|
||||
if (input > factor) {
|
||||
input /= factor;
|
||||
if (binary && input >= 1000) {
|
||||
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + ' k' + i;
|
||||
var prefix = ' k';
|
||||
if (binary) {
|
||||
prefix = ' K';
|
||||
}
|
||||
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + ' k' + i;
|
||||
if (binary && input >= 1000) {
|
||||
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + prefix + i;
|
||||
}
|
||||
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + prefix + i;
|
||||
}
|
||||
return Math.round(input).toLocaleString() + ' ';
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<p translate>Copyright © 2014-2017 the following Contributors:</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, 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., Alexandre Viau, Andrew Dunham, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Harrison Jones, Heiko Zuerker, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jose Manuel Delicado, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Nicholas Rishel, Niels Peter Roest, Nils Jakobi, Pascal Jungblut, Pawel Palenica, Peter Hoeg, Peter Marquardt, Phill Luby, Piotr Bejda, Pramodh KP, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Xavier O., Yannic A., xjtdy888
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Harrison Jones, Heiko Zuerker, Ian Johnson, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Vromans, John Rinehart, Jose Manuel Delicado, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Nicholas Rishel, Niels Peter Roest, Nils Jakobi, Pascal Jungblut, Pawel Palenica, Peter Hoeg, Peter Marquardt, Phill Luby, Piotr Bejda, Pramodh KP, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Xavier O., Yannic A., xjtdy888
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
<div id="log-viewer-log" class="tab-pane in active">
|
||||
<label translate ng-if="logging.logEntries.length == 0">Loading...</label>
|
||||
<textarea ng-focus="logging.paused = true" ng-blur="logging.paused = false" 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>
|
||||
<p translate class="help-block" ng-style="{'visibility': logging.paused ? 'visible' : 'hidden'}">Log tailing paused. Click here to continue.</p>
|
||||
<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>
|
||||
<p translate class="help-block" ng-style="{'visibility': logging.paused ? 'visible' : 'hidden'}">Log tailing paused. Scroll to bottom continue.</p>
|
||||
</div>
|
||||
|
||||
<div id="log-viewer-facilities" class="tab-pane">
|
||||
|
||||
@@ -1097,8 +1097,14 @@ angular.module('syncthing.core')
|
||||
show: function() {
|
||||
$scope.logging.refreshFacilities();
|
||||
$scope.logging.timer = $timeout($scope.logging.fetch);
|
||||
$('#logViewer').modal().on('hidden.bs.modal', function () {
|
||||
var textArea = $('#logViewerText');
|
||||
textArea.on("scroll", $scope.logging.onScroll);
|
||||
$('#logViewer').modal().on('shown.bs.modal', function() {
|
||||
// Scroll to bottom.
|
||||
textArea.scrollTop(textArea[0].scrollHeight);
|
||||
}).on('hidden.bs.modal', function () {
|
||||
$timeout.cancel($scope.logging.timer);
|
||||
textArea.off("scroll", $scope.logging.onScroll);
|
||||
$scope.logging.timer = null;
|
||||
$scope.logging.entries = [];
|
||||
});
|
||||
@@ -1113,6 +1119,14 @@ angular.module('syncthing.core')
|
||||
.success($scope.logging.refreshFacilities)
|
||||
.error($scope.emitHTTPError);
|
||||
},
|
||||
onScroll: function() {
|
||||
var textArea = $('#logViewerText');
|
||||
var scrollTop = textArea.prop('scrollTop');
|
||||
var scrollHeight = textArea.prop('scrollHeight');
|
||||
$scope.logging.paused = scrollHeight > (scrollTop + textArea.outerHeight());
|
||||
// Browser events do not cause redraw, trigger manually.
|
||||
$scope.$apply();
|
||||
},
|
||||
timer: null,
|
||||
entries: [],
|
||||
paused: false,
|
||||
@@ -1125,7 +1139,7 @@ angular.module('syncthing.core')
|
||||
},
|
||||
fetch: function() {
|
||||
var textArea = $('#logViewerText');
|
||||
if (textArea.is(":focus")) {
|
||||
if ($scope.logging.paused) {
|
||||
if (!$scope.logging.timer) return;
|
||||
$scope.logging.timer = $timeout($scope.logging.fetch, 500);
|
||||
return;
|
||||
@@ -1139,11 +1153,14 @@ angular.module('syncthing.core')
|
||||
$http.get(urlbase + '/system/log' + (last ? '?since=' + encodeURIComponent(last) : '')).success(function (data) {
|
||||
if (!$scope.logging.timer) return;
|
||||
$scope.logging.timer = $timeout($scope.logging.fetch, 2000);
|
||||
if (!textArea.is(":focus")) {
|
||||
if (!$scope.logging.paused) {
|
||||
if (data.messages) {
|
||||
$scope.logging.entries.push.apply($scope.logging.entries, data.messages);
|
||||
// Wait for the text area to be redrawn, adding new lines, and then scroll to bottom.
|
||||
$timeout(function() {
|
||||
textArea.scrollTop(textArea[0].scrollHeight);
|
||||
});
|
||||
}
|
||||
textArea.scrollTop(textArea[0].scrollHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+19
@@ -2,6 +2,25 @@
|
||||
* @license AngularJS v1.3.20
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+19
@@ -2,6 +2,25 @@
|
||||
* @license AngularJS v1.3.20
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
(function(window, document, undefined) {'use strict';
|
||||
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2016 Twitter, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012-2018 Dan Grossman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright jQuery Foundation and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -4,5 +4,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:generate go run ../../script/genassets.go -o gui.files.go ../../gui
|
||||
|
||||
// Package auto contains auto generated files for web assets.
|
||||
package auto
|
||||
|
||||
@@ -79,6 +79,9 @@ func (t *tcpListener) Serve() {
|
||||
t.mapping = mapping
|
||||
t.mut.Unlock()
|
||||
|
||||
acceptFailures := 0
|
||||
const maxAcceptFailures = 10
|
||||
|
||||
for {
|
||||
listener.SetDeadline(time.Now().Add(time.Second))
|
||||
conn, err := listener.Accept()
|
||||
@@ -96,10 +99,21 @@ func (t *tcpListener) Serve() {
|
||||
if err != nil {
|
||||
if err, ok := err.(*net.OpError); !ok || !err.Timeout() {
|
||||
l.Warnln("Listen (BEP/tcp): Accepting connection:", err)
|
||||
|
||||
acceptFailures++
|
||||
if acceptFailures > maxAcceptFailures {
|
||||
// Return to restart the listener, because something
|
||||
// seems permanently damaged.
|
||||
return
|
||||
}
|
||||
|
||||
// Slightly increased delay for each failure.
|
||||
time.Sleep(time.Duration(acceptFailures) * time.Second)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
acceptFailures = 0
|
||||
l.Debugln("Listen (BEP/tcp): connect from", conn.RemoteAddr())
|
||||
|
||||
if err := dialer.SetTCPOptions(conn); err != nil {
|
||||
|
||||
+4
-10
@@ -96,11 +96,8 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
|
||||
return true
|
||||
})
|
||||
|
||||
f3.Permissions = f1.Permissions
|
||||
f3.Deleted = f1.Deleted
|
||||
f3.Invalid = f1.Invalid
|
||||
f1.Deleted = true
|
||||
f2.Invalid = true
|
||||
f2.LocalFlags = protocol.FlagLocalMustRescan // one of the invalid markers
|
||||
|
||||
// Should remove
|
||||
err = m.Update([]protocol.FileInfo{f1, f2, f3})
|
||||
@@ -145,14 +142,11 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
|
||||
}
|
||||
|
||||
f1.Deleted = false
|
||||
f1.Invalid = false
|
||||
f1.Permissions = 0
|
||||
f1.LocalFlags = 0
|
||||
f2.Deleted = false
|
||||
f2.Invalid = false
|
||||
f2.Permissions = 0
|
||||
f2.LocalFlags = 0
|
||||
f3.Deleted = false
|
||||
f3.Invalid = false
|
||||
f3.Permissions = 0
|
||||
f3.LocalFlags = 0
|
||||
}
|
||||
|
||||
func TestBlockFinderLookup(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ func (vl VersionList) update(folder, device []byte, file protocol.FileInfo, db *
|
||||
nv := FileVersion{
|
||||
Device: device,
|
||||
Version: file.Version,
|
||||
Invalid: file.Invalid,
|
||||
Invalid: file.IsInvalid(),
|
||||
}
|
||||
for i, v := range vl.Versions {
|
||||
switch v.Version.Compare(file.Version) {
|
||||
|
||||
@@ -9,6 +9,7 @@ package db
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -60,31 +61,32 @@ func Open(file string) (*Instance, error) {
|
||||
// the database and reindexing...
|
||||
l.Infoln("Database corruption detected, unable to recover. Reinitializing...")
|
||||
if err := os.RemoveAll(file); err != nil {
|
||||
return nil, err
|
||||
return nil, errorSuggestion{err, "failed to delete corrupted database"}
|
||||
}
|
||||
db, err = leveldb.OpenFile(file, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errorSuggestion{err, "is another instance of Syncthing running?"}
|
||||
}
|
||||
|
||||
return newDBInstance(db, file), nil
|
||||
return newDBInstance(db, file)
|
||||
}
|
||||
|
||||
func OpenMemory() *Instance {
|
||||
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
return newDBInstance(db, "<memory>")
|
||||
ldb, _ := newDBInstance(db, "<memory>")
|
||||
return ldb
|
||||
}
|
||||
|
||||
func newDBInstance(db *leveldb.DB, location string) *Instance {
|
||||
func newDBInstance(db *leveldb.DB, location string) (*Instance, error) {
|
||||
i := &Instance{
|
||||
DB: db,
|
||||
location: location,
|
||||
}
|
||||
i.folderIdx = newSmallIndex(i, []byte{KeyTypeFolderIdx})
|
||||
i.deviceIdx = newSmallIndex(i, []byte{KeyTypeDeviceIdx})
|
||||
i.updateSchema()
|
||||
return i
|
||||
err := i.updateSchema()
|
||||
return i, err
|
||||
}
|
||||
|
||||
// Committed returns the number of items committed to the database since startup
|
||||
@@ -115,8 +117,9 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, m
|
||||
err = ef.Unmarshal(bs)
|
||||
}
|
||||
|
||||
// The Invalid flag might change without the version being bumped.
|
||||
if err == nil && ef.Version.Equal(f.Version) && ef.Invalid == f.Invalid {
|
||||
// Local flags or the invalid bit might change without the version
|
||||
// being bumped. The IsInvalid() method handles both.
|
||||
if err == nil && ef.Version.Equal(f.Version) && ef.IsInvalid() == f.IsInvalid() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -934,3 +937,12 @@ func resize(k []byte, reqLen int) []byte {
|
||||
}
|
||||
return k[:reqLen]
|
||||
}
|
||||
|
||||
type errorSuggestion struct {
|
||||
inner error
|
||||
suggestion string
|
||||
}
|
||||
|
||||
func (e errorSuggestion) Error() string {
|
||||
return fmt.Sprintf("%s (%s)", e.inner.Error(), e.suggestion)
|
||||
}
|
||||
|
||||
@@ -7,23 +7,52 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
)
|
||||
|
||||
const dbVersion = 4
|
||||
// List of all dbVersion to dbMinSyncthingVersion pairs for convenience
|
||||
// 0: v0.14.0
|
||||
// 1: v0.14.46
|
||||
// 2: v0.14.48
|
||||
// 3: v0.14.49
|
||||
// 4: v0.14.49
|
||||
// 5: v0.14.49
|
||||
// 6: v0.14.50
|
||||
const (
|
||||
dbVersion = 6
|
||||
dbMinSyncthingVersion = "v0.14.50"
|
||||
)
|
||||
|
||||
func (db *Instance) updateSchema() {
|
||||
type databaseDowngradeError struct {
|
||||
minSyncthingVersion string
|
||||
}
|
||||
|
||||
func (e databaseDowngradeError) Error() string {
|
||||
if e.minSyncthingVersion == "" {
|
||||
return "newer Syncthing required"
|
||||
}
|
||||
return fmt.Sprintf("Syncthing %s required", e.minSyncthingVersion)
|
||||
}
|
||||
|
||||
func (db *Instance) updateSchema() error {
|
||||
miscDB := NewNamespacedKV(db, string(KeyTypeMiscData))
|
||||
prevVersion, _ := miscDB.Int64("dbVersion")
|
||||
|
||||
if prevVersion >= dbVersion {
|
||||
return
|
||||
if prevVersion > dbVersion {
|
||||
err := databaseDowngradeError{}
|
||||
if minSyncthingVersion, ok := miscDB.String("dbMinSyncthingVersion"); ok {
|
||||
err.minSyncthingVersion = minSyncthingVersion
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
l.Infof("Updating database schema version from %v to %v...", prevVersion, dbVersion)
|
||||
if prevVersion == dbVersion {
|
||||
return nil
|
||||
}
|
||||
|
||||
if prevVersion < 1 {
|
||||
db.updateSchema0to1()
|
||||
@@ -34,12 +63,18 @@ func (db *Instance) updateSchema() {
|
||||
if prevVersion < 3 {
|
||||
db.updateSchema2to3()
|
||||
}
|
||||
// This update fixes a problem that only exists in dbVersion 3.
|
||||
if prevVersion == 3 {
|
||||
db.updateSchema3to4()
|
||||
// This update fixes problems existing in versions 3 and 4
|
||||
if prevVersion == 3 || prevVersion == 4 {
|
||||
db.updateSchemaTo5()
|
||||
}
|
||||
if prevVersion < 6 {
|
||||
db.updateSchema5to6()
|
||||
}
|
||||
|
||||
miscDB.PutInt64("dbVersion", dbVersion)
|
||||
miscDB.PutString("dbMinSyncthingVersion", dbMinSyncthingVersion)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Instance) updateSchema0to1() {
|
||||
@@ -94,7 +129,7 @@ func (db *Instance) updateSchema0to1() {
|
||||
}
|
||||
|
||||
// Add invalid files to global list
|
||||
if f.Invalid {
|
||||
if f.IsInvalid() {
|
||||
gk = db.globalKeyInto(gk, folder, name)
|
||||
if t.updateGlobal(gk, folder, device, f, meta) {
|
||||
if _, ok := changedFolders[string(folder)]; !ok {
|
||||
@@ -158,10 +193,11 @@ func (db *Instance) updateSchema2to3() {
|
||||
}
|
||||
}
|
||||
|
||||
// updateSchema3to4 resets the need bucket due a bug existing in dbVersion 3 /
|
||||
// v0.14.49-rc.1
|
||||
// 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
|
||||
func (db *Instance) updateSchema3to4() {
|
||||
// https://github.com/syncthing/syncthing/issues/5053
|
||||
func (db *Instance) updateSchemaTo5() {
|
||||
t := db.newReadWriteTransaction()
|
||||
var nk []byte
|
||||
for _, folderStr := range db.ListFolders() {
|
||||
@@ -172,3 +208,33 @@ func (db *Instance) updateSchema3to4() {
|
||||
|
||||
db.updateSchema2to3()
|
||||
}
|
||||
|
||||
func (db *Instance) updateSchema5to6() {
|
||||
// For every local file with the Invalid bit set, clear the Invalid bit and
|
||||
// set LocalFlags = FlagLocalIgnored.
|
||||
|
||||
t := db.newReadWriteTransaction()
|
||||
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 {
|
||||
if !f.IsInvalid() {
|
||||
return true
|
||||
}
|
||||
|
||||
fi := f.(protocol.FileInfo)
|
||||
fi.RawInvalid = false
|
||||
fi.LocalFlags = protocol.FlagLocalIgnored
|
||||
bs, _ := fi.Marshal()
|
||||
|
||||
dk = db.deviceKeyInto(dk, folder, protocol.LocalDeviceID[:], []byte(fi.Name))
|
||||
t.Put(dk, bs)
|
||||
|
||||
t.checkFlush()
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+53
-10
@@ -8,6 +8,7 @@ package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
@@ -154,12 +155,12 @@ func TestDropIndexIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidFiles(t *testing.T) {
|
||||
func TestIgnoredFiles(t *testing.T) {
|
||||
ldb, err := openJSONS("testdata/v0.14.48-ignoredfiles.db.jsons")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := newDBInstance(ldb, "<memory>")
|
||||
db, _ := newDBInstance(ldb, "<memory>")
|
||||
fs := NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
|
||||
|
||||
// The contents of the database are like this:
|
||||
@@ -192,27 +193,42 @@ func TestInvalidFiles(t *testing.T) {
|
||||
// },
|
||||
// })
|
||||
|
||||
// Local files should have the "ignored" bit in addition to just being
|
||||
// generally invalid if we want to look at the simulation of that bit.
|
||||
|
||||
fi, ok := fs.Get(protocol.LocalDeviceID, "foo")
|
||||
if !ok {
|
||||
t.Fatal("foo should exist")
|
||||
}
|
||||
if !fi.Invalid {
|
||||
if !fi.IsInvalid() {
|
||||
t.Error("foo should be invalid")
|
||||
}
|
||||
if !fi.IsIgnored() {
|
||||
t.Error("foo should be ignored")
|
||||
}
|
||||
|
||||
fi, ok = fs.Get(protocol.LocalDeviceID, "bar")
|
||||
if !ok {
|
||||
t.Fatal("bar should exist")
|
||||
}
|
||||
if fi.Invalid {
|
||||
if fi.IsInvalid() {
|
||||
t.Error("bar should not be invalid")
|
||||
}
|
||||
if fi.IsIgnored() {
|
||||
t.Error("bar should not be ignored")
|
||||
}
|
||||
|
||||
// Remote files have the invalid bit as usual, and the IsInvalid() method
|
||||
// should pick this up too.
|
||||
|
||||
fi, ok = fs.Get(protocol.DeviceID{42}, "baz")
|
||||
if !ok {
|
||||
t.Fatal("baz should exist")
|
||||
}
|
||||
if !fi.Invalid {
|
||||
if !fi.IsInvalid() {
|
||||
t.Error("baz should be invalid")
|
||||
}
|
||||
if !fi.IsInvalid() {
|
||||
t.Error("baz should be invalid")
|
||||
}
|
||||
|
||||
@@ -220,7 +236,10 @@ func TestInvalidFiles(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatal("quux should exist")
|
||||
}
|
||||
if fi.Invalid {
|
||||
if fi.IsInvalid() {
|
||||
t.Error("quux should not be invalid")
|
||||
}
|
||||
if fi.IsInvalid() {
|
||||
t.Error("quux should not be invalid")
|
||||
}
|
||||
}
|
||||
@@ -245,13 +264,13 @@ func init() {
|
||||
},
|
||||
remoteDevice0: {
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
||||
},
|
||||
remoteDevice1: {
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -262,7 +281,7 @@ func TestUpdate0to3(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := newDBInstance(ldb, "<memory>")
|
||||
db, _ := newDBInstance(ldb, "<memory>")
|
||||
|
||||
folder := []byte(update0to3Folder)
|
||||
|
||||
@@ -320,3 +339,27 @@ func TestUpdate0to3(t *testing.T) {
|
||||
t.Errorf(`Missing needed file "%v"`, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDowngrade(t *testing.T) {
|
||||
loc := "testdata/downgrade.db"
|
||||
db, err := Open(loc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
db.Close()
|
||||
os.RemoveAll(loc)
|
||||
}()
|
||||
|
||||
miscDB := NewNamespacedKV(db, string(KeyTypeMiscData))
|
||||
miscDB.PutInt64("dbVersion", dbVersion+1)
|
||||
l.Infoln(dbVersion)
|
||||
|
||||
db.Close()
|
||||
db, err = Open(loc)
|
||||
if err, ok := err.(databaseDowngradeError); !ok {
|
||||
t.Fatal("Expected error due to database downgrade, got", err)
|
||||
} else if err.minSyncthingVersion != dbMinSyncthingVersion {
|
||||
t.Fatalf("Error has %v as min Syncthing version, expected %v", err.minSyncthingVersion, dbMinSyncthingVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (t readWriteTransaction) insertFile(fk, folder, device []byte, file protoco
|
||||
// file. If the device is already present in the list, the version is updated.
|
||||
// If the file does not have an entry in the global list, it is created.
|
||||
func (t readWriteTransaction) updateGlobal(gk, folder, device []byte, file protocol.FileInfo, meta *metadataTracker) bool {
|
||||
l.Debugf("update global; folder=%q device=%v file=%q version=%v invalid=%v", folder, protocol.DeviceIDFromBytes(device), file.Name, file.Version, file.Invalid)
|
||||
l.Debugf("update global; folder=%q device=%v file=%q version=%v invalid=%v", folder, protocol.DeviceIDFromBytes(device), file.Name, file.Version, file.IsInvalid())
|
||||
|
||||
var fl VersionList
|
||||
if svl, err := t.Get(gk, nil); err == nil {
|
||||
@@ -99,11 +99,31 @@ func (t readWriteTransaction) updateGlobal(gk, folder, device []byte, file proto
|
||||
|
||||
name := []byte(file.Name)
|
||||
|
||||
if removedAt != 0 && insertedAt != 0 {
|
||||
if bytes.Equal(device, protocol.LocalDeviceID[:]) && file.Version.Equal(fl.Versions[0].Version) {
|
||||
l.Debugf("local need delete; folder=%q, name=%q", folder, name)
|
||||
t.Delete(t.db.needKey(folder, name))
|
||||
var newGlobal protocol.FileInfo
|
||||
if insertedAt == 0 {
|
||||
// Inserted a new newest version
|
||||
newGlobal = file
|
||||
} else if new, ok := t.getFile(folder, fl.Versions[0].Device, name); ok {
|
||||
// The previous second version is now the first
|
||||
newGlobal = new
|
||||
} else {
|
||||
panic("This file must exist in the db")
|
||||
}
|
||||
|
||||
// Fixup the list of files we need.
|
||||
nk := t.db.needKey(folder, name)
|
||||
hasNeeded, _ := t.db.Has(nk, nil)
|
||||
if localFV, haveLocalFV := fl.Get(protocol.LocalDeviceID[:]); need(newGlobal, haveLocalFV, localFV.Version) {
|
||||
if !hasNeeded {
|
||||
l.Debugf("local need insert; folder=%q, name=%q", folder, name)
|
||||
t.Put(nk, nil)
|
||||
}
|
||||
} else if hasNeeded {
|
||||
l.Debugf("local need delete; folder=%q, name=%q", folder, name)
|
||||
t.Delete(nk)
|
||||
}
|
||||
|
||||
if removedAt != 0 && insertedAt != 0 {
|
||||
l.Debugf(`new global for "%v" after update: %v`, file.Name, fl)
|
||||
t.Put(gk, mustMarshal(&fl))
|
||||
return true
|
||||
@@ -124,31 +144,8 @@ func (t readWriteTransaction) updateGlobal(gk, folder, device []byte, file proto
|
||||
}
|
||||
|
||||
// Add the new global to the global size counter
|
||||
var newGlobal protocol.FileInfo
|
||||
if insertedAt == 0 {
|
||||
// Inserted a new newest version
|
||||
newGlobal = file
|
||||
} else if new, ok := t.getFile(folder, fl.Versions[0].Device, name); ok {
|
||||
// The previous second version is now the first
|
||||
newGlobal = new
|
||||
} else {
|
||||
panic("This file must exist in the db")
|
||||
}
|
||||
meta.addFile(globalDeviceID, newGlobal)
|
||||
|
||||
// Fixup the list of files we need.
|
||||
nk := t.db.needKey(folder, name)
|
||||
hasNeeded, _ := t.db.Has(nk, nil)
|
||||
if localFV, haveLocalFV := fl.Get(protocol.LocalDeviceID[:]); need(newGlobal, haveLocalFV, localFV.Version) {
|
||||
if !hasNeeded {
|
||||
l.Debugf("local need insert; folder=%q, name=%q", folder, name)
|
||||
t.Put(nk, nil)
|
||||
}
|
||||
} else if hasNeeded {
|
||||
l.Debugf("local need delete; folder=%q, name=%q", folder, name)
|
||||
t.Delete(nk)
|
||||
}
|
||||
|
||||
l.Debugf(`new global for "%v" after update: %v`, file.Name, fl)
|
||||
t.Put(gk, mustMarshal(&fl))
|
||||
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) {
|
||||
for _, nf := range oldFs {
|
||||
dk = s.db.deviceKeyInto(dk, folder, device[:], []byte(osutil.NormalizedFilename(nf.Name)))
|
||||
ef, ok := s.db.getFile(dk)
|
||||
if ok && ef.Version.Equal(nf.Version) && ef.Invalid == nf.Invalid {
|
||||
if ok && ef.Version.Equal(nf.Version) && ef.IsInvalid() == nf.IsInvalid() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
+67
-34
@@ -117,7 +117,7 @@ func (l fileList) String() string {
|
||||
func TestGlobalSet(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
local0 := fileList{
|
||||
protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
||||
@@ -332,20 +332,20 @@ func TestGlobalSet(t *testing.T) {
|
||||
func TestNeedWithInvalid(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
localHave := fileList{
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
||||
}
|
||||
remote0Have := fileList{
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
||||
}
|
||||
remote1Have := fileList{
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
}
|
||||
|
||||
expectedNeed := fileList{
|
||||
@@ -369,16 +369,16 @@ func TestNeedWithInvalid(t *testing.T) {
|
||||
func TestUpdateToInvalid(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
folder := "test)"
|
||||
folder := "test"
|
||||
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
f := db.NewBlockFinder(ldb)
|
||||
|
||||
localHave := fileList{
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), LocalFlags: protocol.FlagLocalIgnored},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Invalid: true},
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, LocalFlags: protocol.FlagLocalIgnored},
|
||||
}
|
||||
|
||||
replace(s, protocol.LocalDeviceID, localHave)
|
||||
@@ -391,10 +391,13 @@ func TestUpdateToInvalid(t *testing.T) {
|
||||
}
|
||||
|
||||
oldBlockHash := localHave[1].Blocks[0].Hash
|
||||
localHave[1].Invalid = true
|
||||
|
||||
localHave[1].LocalFlags = protocol.FlagLocalIgnored
|
||||
localHave[1].Blocks = nil
|
||||
localHave[4].Invalid = false
|
||||
|
||||
localHave[4].LocalFlags = 0
|
||||
localHave[4].Blocks = genBlocks(3)
|
||||
|
||||
s.Update(protocol.LocalDeviceID, append(fileList{}, localHave[1], localHave[4]))
|
||||
|
||||
have = fileList(haveList(s, protocol.LocalDeviceID))
|
||||
@@ -425,19 +428,19 @@ func TestUpdateToInvalid(t *testing.T) {
|
||||
func TestInvalidAvailability(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
remote0Have := fileList{
|
||||
protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
}
|
||||
remote1Have := fileList{
|
||||
protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
}
|
||||
|
||||
replace(s, remoteDevice0, remote0Have)
|
||||
@@ -463,7 +466,7 @@ func TestInvalidAvailability(t *testing.T) {
|
||||
func TestGlobalReset(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
local := []protocol.FileInfo{
|
||||
{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
|
||||
@@ -501,7 +504,7 @@ func TestGlobalReset(t *testing.T) {
|
||||
func TestNeed(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
local := []protocol.FileInfo{
|
||||
{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
|
||||
@@ -539,7 +542,7 @@ func TestNeed(t *testing.T) {
|
||||
func TestSequence(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
local1 := []protocol.FileInfo{
|
||||
{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
|
||||
@@ -623,7 +626,7 @@ func TestGlobalNeedWithInvalid(t *testing.T) {
|
||||
|
||||
rem0 := fileList{
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: remoteDevice0.Short(), Value: 1002}}}},
|
||||
}
|
||||
@@ -632,8 +635,8 @@ func TestGlobalNeedWithInvalid(t *testing.T) {
|
||||
rem1 := fileList{
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true, ModifiedS: 10},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true, ModifiedS: 10},
|
||||
}
|
||||
replace(s, remoteDevice1, rem1)
|
||||
|
||||
@@ -690,7 +693,7 @@ func TestCommitted(t *testing.T) {
|
||||
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
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}}}},
|
||||
@@ -736,7 +739,7 @@ func BenchmarkUpdateOneFile(b *testing.B) {
|
||||
os.RemoveAll("testdata/benchmarkupdate.db")
|
||||
}()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
replace(m, protocol.LocalDeviceID, local0)
|
||||
l := local0[4:5]
|
||||
|
||||
@@ -751,7 +754,7 @@ func BenchmarkUpdateOneFile(b *testing.B) {
|
||||
func TestIndexID(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
// The Index ID for some random device is zero by default.
|
||||
id := s.IndexID(remoteDevice0)
|
||||
@@ -783,7 +786,7 @@ func TestIndexID(t *testing.T) {
|
||||
func TestDropFiles(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
local0 := fileList{
|
||||
protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
||||
@@ -846,11 +849,11 @@ func TestDropFiles(t *testing.T) {
|
||||
func TestIssue4701(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
localHave := fileList{
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Invalid: true},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, LocalFlags: protocol.FlagLocalIgnored},
|
||||
}
|
||||
|
||||
s.Update(protocol.LocalDeviceID, localHave)
|
||||
@@ -862,7 +865,7 @@ func TestIssue4701(t *testing.T) {
|
||||
t.Errorf("Expected 1 global file, got %v", c.Files)
|
||||
}
|
||||
|
||||
localHave[1].Invalid = false
|
||||
localHave[1].LocalFlags = 0
|
||||
s.Update(protocol.LocalDeviceID, localHave)
|
||||
|
||||
if c := s.LocalSize(); c.Files != 2 {
|
||||
@@ -872,8 +875,8 @@ func TestIssue4701(t *testing.T) {
|
||||
t.Errorf("Expected 2 global files, got %v", c.Files)
|
||||
}
|
||||
|
||||
localHave[0].Invalid = true
|
||||
localHave[1].Invalid = true
|
||||
localHave[0].LocalFlags = protocol.FlagLocalIgnored
|
||||
localHave[1].LocalFlags = protocol.FlagLocalIgnored
|
||||
s.Update(protocol.LocalDeviceID, localHave)
|
||||
|
||||
if c := s.LocalSize(); c.Files != 0 {
|
||||
@@ -887,16 +890,16 @@ func TestIssue4701(t *testing.T) {
|
||||
func TestWithHaveSequence(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
folder := "test)"
|
||||
folder := "test"
|
||||
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
// The files must not be in alphabetical order
|
||||
localHave := fileList{
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Invalid: true},
|
||||
protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, RawInvalid: true},
|
||||
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
||||
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
||||
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
|
||||
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
|
||||
}
|
||||
|
||||
replace(s, protocol.LocalDeviceID, localHave)
|
||||
@@ -1018,7 +1021,7 @@ func TestIssue5007(t *testing.T) {
|
||||
t.Fatalf("Local need incorrect;\n A: %v !=\n E: %v", need[0], fs[0])
|
||||
}
|
||||
|
||||
fs[0].Invalid = true
|
||||
fs[0].LocalFlags = protocol.FlagLocalIgnored
|
||||
s.Update(protocol.LocalDeviceID, fs)
|
||||
|
||||
if need := needList(s, protocol.LocalDeviceID); len(need) != 0 {
|
||||
@@ -1062,6 +1065,36 @@ func TestNeedDeleted(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeedAfterUnignore(t *testing.T) {
|
||||
ldb := db.OpenMemory()
|
||||
|
||||
folder := "test"
|
||||
file := "foo"
|
||||
s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb)
|
||||
|
||||
remID := remoteDevice0.Short()
|
||||
|
||||
// Initial state: Devices in sync, locally ignored
|
||||
local := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10}
|
||||
local.SetIgnored(myID)
|
||||
remote := protocol.FileInfo{Name: file, Version: protocol.Vector{Counters: []protocol.Counter{{ID: remID, Value: 1}, {ID: myID, Value: 1}}}, ModifiedS: 10}
|
||||
s.Update(protocol.LocalDeviceID, fileList{local})
|
||||
s.Update(remoteDevice0, fileList{remote})
|
||||
|
||||
// Unignore locally -> conflicting changes. Remote is newer, thus winning.
|
||||
local.Version = local.Version.Update(myID)
|
||||
local.Version = local.Version.DropOthers(myID)
|
||||
local.LocalFlags = 0
|
||||
local.ModifiedS = 0
|
||||
s.Update(protocol.LocalDeviceID, fileList{local})
|
||||
|
||||
if need := needList(s, protocol.LocalDeviceID); len(need) != 1 {
|
||||
t.Fatal("Expected one local need, got", need)
|
||||
} else if !need[0].IsEquivalent(remote, false, false) {
|
||||
t.Fatalf("Got %v, expected %v", need[0], remote)
|
||||
}
|
||||
}
|
||||
|
||||
func replace(fs *db.FileSet, device protocol.DeviceID, files []protocol.FileInfo) {
|
||||
fs.Drop(device)
|
||||
fs.Update(device, files)
|
||||
|
||||
+25
-6
@@ -17,8 +17,19 @@ import (
|
||||
)
|
||||
|
||||
func (f FileInfoTruncated) String() string {
|
||||
return fmt.Sprintf("File{Name:%q, Permissions:0%o, Modified:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, BlockSize:%d}",
|
||||
f.Name, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.RawBlockSize)
|
||||
switch f.Type {
|
||||
case protocol.FileInfoTypeDirectory:
|
||||
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions)
|
||||
case protocol.FileInfoTypeFile:
|
||||
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize)
|
||||
case protocol.FileInfoTypeSymlink, protocol.FileInfoTypeDeprecatedSymlinkDirectory, protocol.FileInfoTypeDeprecatedSymlinkFile:
|
||||
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q}",
|
||||
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget)
|
||||
default:
|
||||
panic("mystery file type detected")
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) IsDeleted() bool {
|
||||
@@ -26,7 +37,15 @@ func (f FileInfoTruncated) IsDeleted() bool {
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) IsInvalid() bool {
|
||||
return f.Invalid
|
||||
return f.RawInvalid || f.LocalFlags&protocol.LocalInvalidFlags != 0
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) IsIgnored() bool {
|
||||
return f.LocalFlags&protocol.FlagLocalIgnored != 0
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) MustRescan() bool {
|
||||
return f.LocalFlags&protocol.FlagLocalMustRescan != 0
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) IsDirectory() bool {
|
||||
@@ -79,15 +98,15 @@ func (f FileInfoTruncated) FileVersion() protocol.Vector {
|
||||
return f.Version
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) ConvertToInvalidFileInfo(invalidatedBy protocol.ShortID) protocol.FileInfo {
|
||||
func (f FileInfoTruncated) ConvertToIgnoredFileInfo(by protocol.ShortID) protocol.FileInfo {
|
||||
return protocol.FileInfo{
|
||||
Name: f.Name,
|
||||
Type: f.Type,
|
||||
ModifiedS: f.ModifiedS,
|
||||
ModifiedNs: f.ModifiedNs,
|
||||
ModifiedBy: invalidatedBy,
|
||||
Invalid: true,
|
||||
ModifiedBy: by,
|
||||
Version: f.Version,
|
||||
RawBlockSize: f.RawBlockSize,
|
||||
LocalFlags: protocol.FlagLocalIgnored,
|
||||
}
|
||||
}
|
||||
|
||||
+80
-47
@@ -66,13 +66,15 @@ type FileInfoTruncated struct {
|
||||
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
|
||||
ModifiedBy github_com_syncthing_syncthing_lib_protocol.ShortID `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.ShortID" json:"modified_by"`
|
||||
Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||
Invalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
|
||||
RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
|
||||
NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"`
|
||||
Version protocol.Vector `protobuf:"bytes,9,opt,name=version" json:"version"`
|
||||
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||
RawBlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
|
||||
// repeated BlockInfo Blocks = 16
|
||||
SymlinkTarget string `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
|
||||
// see bep.proto
|
||||
LocalFlags uint32 `protobuf:"varint,1000,opt,name=local_flags,json=localFlags,proto3" json:"local_flags,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FileInfoTruncated) Reset() { *m = FileInfoTruncated{} }
|
||||
@@ -236,10 +238,10 @@ func (m *FileInfoTruncated) MarshalTo(dAtA []byte) (int, error) {
|
||||
}
|
||||
i++
|
||||
}
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
dAtA[i] = 0x38
|
||||
i++
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
@@ -292,6 +294,13 @@ func (m *FileInfoTruncated) MarshalTo(dAtA []byte) (int, error) {
|
||||
i = encodeVarintStructs(dAtA, i, uint64(len(m.SymlinkTarget)))
|
||||
i += copy(dAtA[i:], m.SymlinkTarget)
|
||||
}
|
||||
if m.LocalFlags != 0 {
|
||||
dAtA[i] = 0xc0
|
||||
i++
|
||||
dAtA[i] = 0x3e
|
||||
i++
|
||||
i = encodeVarintStructs(dAtA, i, uint64(m.LocalFlags))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -462,7 +471,7 @@ func (m *FileInfoTruncated) ProtoSize() (n int) {
|
||||
if m.Deleted {
|
||||
n += 2
|
||||
}
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
n += 2
|
||||
}
|
||||
if m.NoPermissions {
|
||||
@@ -486,6 +495,9 @@ func (m *FileInfoTruncated) ProtoSize() (n int) {
|
||||
if l > 0 {
|
||||
n += 2 + l + sovStructs(uint64(l))
|
||||
}
|
||||
if m.LocalFlags != 0 {
|
||||
n += 2 + sovStructs(uint64(m.LocalFlags))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -913,7 +925,7 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error {
|
||||
m.Deleted = bool(v != 0)
|
||||
case 7:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RawInvalid", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -930,7 +942,7 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Invalid = bool(v != 0)
|
||||
m.RawInvalid = bool(v != 0)
|
||||
case 8:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType)
|
||||
@@ -1086,6 +1098,25 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.SymlinkTarget = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 1000:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LocalFlags", wireType)
|
||||
}
|
||||
m.LocalFlags = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowStructs
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.LocalFlags |= (uint32(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipStructs(dAtA[iNdEx:])
|
||||
@@ -1510,45 +1541,47 @@ var (
|
||||
func init() { proto.RegisterFile("structs.proto", fileDescriptorStructs) }
|
||||
|
||||
var fileDescriptorStructs = []byte{
|
||||
// 630 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcf, 0x6a, 0xdb, 0x4c,
|
||||
0x10, 0xb7, 0x62, 0xc9, 0xb1, 0x47, 0x76, 0xbe, 0x64, 0x09, 0x41, 0x18, 0x3e, 0x5b, 0x18, 0x0a,
|
||||
0xa2, 0x50, 0xb9, 0x4d, 0xe8, 0xa5, 0xbd, 0xb9, 0x21, 0x10, 0x28, 0x6d, 0x59, 0x87, 0x9c, 0x0a,
|
||||
0xc6, 0x92, 0xd6, 0xce, 0x12, 0x79, 0xd7, 0xd1, 0xae, 0x52, 0x94, 0x27, 0xe9, 0x31, 0x4f, 0xd0,
|
||||
0xa7, 0xe8, 0x21, 0xc7, 0x9e, 0x7b, 0x08, 0xad, 0xfb, 0x22, 0x45, 0xbb, 0x92, 0xa2, 0xf4, 0xd4,
|
||||
0xde, 0xe6, 0x37, 0xff, 0x67, 0x7e, 0x33, 0xd0, 0x13, 0x32, 0x49, 0x43, 0x29, 0xfc, 0x75, 0xc2,
|
||||
0x25, 0x47, 0x5b, 0x51, 0xd0, 0x7f, 0xb6, 0xa4, 0xf2, 0x22, 0x0d, 0xfc, 0x90, 0xaf, 0xc6, 0x4b,
|
||||
0xbe, 0xe4, 0x63, 0x65, 0x0a, 0xd2, 0x85, 0x42, 0x0a, 0x28, 0x49, 0x87, 0xf4, 0x5f, 0xd6, 0xdc,
|
||||
0x45, 0xc6, 0x42, 0x79, 0x41, 0xd9, 0xb2, 0x26, 0xc5, 0x34, 0xd0, 0x19, 0x42, 0x1e, 0x8f, 0x03,
|
||||
0xb2, 0xd6, 0x61, 0xa3, 0x2b, 0xb0, 0x4f, 0x68, 0x4c, 0xce, 0x49, 0x22, 0x28, 0x67, 0xe8, 0x39,
|
||||
0x6c, 0x5f, 0x6b, 0xd1, 0x31, 0x5c, 0xc3, 0xb3, 0x0f, 0x77, 0xfd, 0x32, 0xc8, 0x3f, 0x27, 0xa1,
|
||||
0xe4, 0xc9, 0xc4, 0xbc, 0xbb, 0x1f, 0x36, 0x70, 0xe9, 0x86, 0x0e, 0xa0, 0x15, 0x91, 0x6b, 0x1a,
|
||||
0x12, 0x67, 0xcb, 0x35, 0xbc, 0x2e, 0x2e, 0x10, 0x72, 0x60, 0x9b, 0xb2, 0xeb, 0x79, 0x4c, 0x23,
|
||||
0xa7, 0xe9, 0x1a, 0x5e, 0x1b, 0x97, 0x70, 0x74, 0x02, 0x76, 0x51, 0xee, 0x2d, 0x15, 0x12, 0xbd,
|
||||
0x80, 0x76, 0x91, 0x4b, 0x38, 0x86, 0xdb, 0xf4, 0xec, 0xc3, 0xff, 0xfc, 0x28, 0xf0, 0x6b, 0x5d,
|
||||
0x15, 0x25, 0x2b, 0xb7, 0x57, 0xe6, 0xe7, 0xdb, 0x61, 0x63, 0xf4, 0xc5, 0x84, 0xbd, 0xdc, 0xeb,
|
||||
0x94, 0x2d, 0xf8, 0x59, 0x92, 0xb2, 0x70, 0x2e, 0x49, 0x84, 0x10, 0x98, 0x6c, 0xbe, 0x22, 0xaa,
|
||||
0xfd, 0x0e, 0x56, 0x32, 0x7a, 0x0a, 0xa6, 0xcc, 0xd6, 0xba, 0xc3, 0x9d, 0xc3, 0x83, 0x87, 0x91,
|
||||
0xaa, 0xf0, 0x6c, 0x4d, 0xb0, 0xf2, 0xc9, 0xe3, 0x05, 0xbd, 0x21, 0xaa, 0xe9, 0x26, 0x56, 0x32,
|
||||
0x72, 0xc1, 0x5e, 0x93, 0x64, 0x45, 0x85, 0xee, 0xd2, 0x74, 0x0d, 0xaf, 0x87, 0xeb, 0x2a, 0xf4,
|
||||
0x3f, 0xc0, 0x8a, 0x47, 0x74, 0x41, 0x49, 0x34, 0x13, 0x8e, 0xa5, 0x62, 0x3b, 0xa5, 0x66, 0x9a,
|
||||
0x2f, 0x23, 0x22, 0x31, 0x91, 0x24, 0x72, 0x5a, 0x7a, 0x19, 0x05, 0xac, 0xaf, 0x69, 0xfb, 0xd1,
|
||||
0x9a, 0xd0, 0x13, 0xd8, 0x61, 0x7c, 0x56, 0xaf, 0xdb, 0x56, 0x0e, 0x3d, 0xc6, 0x3f, 0xd4, 0x2a,
|
||||
0xd7, 0x18, 0xeb, 0xfc, 0x1d, 0x63, 0x7d, 0x68, 0x0b, 0x72, 0x95, 0x12, 0x16, 0x12, 0x07, 0x54,
|
||||
0xa7, 0x15, 0x46, 0x43, 0xb0, 0xab, 0x39, 0x98, 0x70, 0x6c, 0xd7, 0xf0, 0x2c, 0x5c, 0x8d, 0xf6,
|
||||
0x4e, 0xa0, 0x8f, 0x35, 0x87, 0x20, 0x73, 0xba, 0xae, 0xe1, 0x99, 0x93, 0xd7, 0x79, 0x81, 0xef,
|
||||
0xf7, 0xc3, 0xa3, 0x7f, 0xb8, 0x41, 0x7f, 0x7a, 0xc1, 0x13, 0x79, 0x7a, 0xfc, 0x90, 0x7d, 0x92,
|
||||
0xa1, 0x31, 0x40, 0x10, 0xf3, 0xf0, 0x72, 0xa6, 0x28, 0xe8, 0xe5, 0xd5, 0x27, 0xbb, 0x9b, 0xfb,
|
||||
0x61, 0x17, 0xcf, 0x3f, 0x4d, 0x72, 0xc3, 0x94, 0xde, 0x10, 0xdc, 0x09, 0x4a, 0x31, 0x5f, 0x92,
|
||||
0xc8, 0x56, 0x31, 0x65, 0x97, 0x33, 0x39, 0x4f, 0x96, 0x44, 0x3a, 0x7b, 0x8a, 0xf7, 0x5e, 0xa1,
|
||||
0x3d, 0x53, 0xca, 0xe2, 0x60, 0xbe, 0x1a, 0xd0, 0x7a, 0xc3, 0x53, 0x26, 0x05, 0xda, 0x07, 0x6b,
|
||||
0x41, 0x63, 0x22, 0xd4, 0x99, 0x58, 0x58, 0x83, 0x9c, 0xe7, 0x88, 0x26, 0x6a, 0x69, 0x94, 0x08,
|
||||
0x75, 0x2e, 0x16, 0xae, 0xab, 0xd4, 0xee, 0x74, 0x66, 0xa1, 0x2e, 0xc4, 0xc2, 0x15, 0xae, 0x93,
|
||||
0x6c, 0x2a, 0x53, 0x45, 0xf2, 0x3e, 0x58, 0x41, 0x26, 0x49, 0x79, 0x18, 0x1a, 0x3c, 0xe2, 0xa1,
|
||||
0xf5, 0x07, 0x0f, 0x7d, 0x68, 0xeb, 0x3f, 0x3a, 0x3d, 0x56, 0x13, 0x75, 0x71, 0x85, 0x47, 0xef,
|
||||
0xa1, 0xa3, 0xa7, 0x98, 0x12, 0x89, 0x3c, 0x68, 0x85, 0x0a, 0x14, 0xbf, 0x03, 0xf9, 0xef, 0x68,
|
||||
0x73, 0xc1, 0x7b, 0x61, 0xcf, 0xdb, 0x0b, 0x13, 0x92, 0xff, 0x88, 0x1a, 0xac, 0x89, 0x4b, 0x38,
|
||||
0xd9, 0xbf, 0xfb, 0x39, 0x68, 0xdc, 0x6d, 0x06, 0xc6, 0xb7, 0xcd, 0xc0, 0xf8, 0xb1, 0x19, 0x34,
|
||||
0x6e, 0x7f, 0x0d, 0x8c, 0xa0, 0xa5, 0x98, 0x3a, 0xfa, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x28, 0xfa,
|
||||
0x1e, 0x63, 0x9b, 0x04, 0x00, 0x00,
|
||||
// 663 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcd, 0x6a, 0xdb, 0x40,
|
||||
0x10, 0xb6, 0x62, 0xf9, 0x6f, 0x6c, 0xa7, 0xc9, 0x12, 0x82, 0x30, 0xd4, 0x16, 0x86, 0x82, 0x28,
|
||||
0xd4, 0x6e, 0x13, 0x7a, 0x69, 0x6f, 0x6a, 0x08, 0x18, 0x4a, 0x5b, 0xd6, 0x21, 0xa7, 0x82, 0xd1,
|
||||
0xcf, 0xda, 0x59, 0x22, 0x6b, 0x1d, 0xed, 0x3a, 0x41, 0x79, 0x92, 0x1e, 0xf3, 0x30, 0x3d, 0xe4,
|
||||
0xd8, 0x73, 0x0f, 0x26, 0x75, 0x2f, 0x7d, 0x8c, 0xb2, 0xbb, 0x92, 0xa2, 0xf6, 0xd4, 0xde, 0xe6,
|
||||
0x9b, 0x9f, 0x9d, 0x6f, 0x66, 0xbe, 0x85, 0x2e, 0x17, 0xc9, 0x3a, 0x10, 0x7c, 0xb4, 0x4a, 0x98,
|
||||
0x60, 0x68, 0x27, 0xf4, 0x7b, 0x2f, 0x16, 0x54, 0x5c, 0xac, 0xfd, 0x51, 0xc0, 0x96, 0xe3, 0x05,
|
||||
0x5b, 0xb0, 0xb1, 0x0a, 0xf9, 0xeb, 0xb9, 0x42, 0x0a, 0x28, 0x4b, 0x97, 0xf4, 0x5e, 0x97, 0xd2,
|
||||
0x79, 0x1a, 0x07, 0xe2, 0x82, 0xc6, 0x8b, 0x92, 0x15, 0x51, 0x5f, 0xbf, 0x10, 0xb0, 0x68, 0xec,
|
||||
0x93, 0x95, 0x2e, 0x1b, 0x5e, 0x41, 0xfb, 0x94, 0x46, 0xe4, 0x9c, 0x24, 0x9c, 0xb2, 0x18, 0xbd,
|
||||
0x84, 0xc6, 0xb5, 0x36, 0x2d, 0xc3, 0x36, 0x9c, 0xf6, 0xd1, 0xde, 0x28, 0x2f, 0x1a, 0x9d, 0x93,
|
||||
0x40, 0xb0, 0xc4, 0x35, 0xef, 0x37, 0x83, 0x0a, 0xce, 0xd3, 0xd0, 0x21, 0xd4, 0x43, 0x72, 0x4d,
|
||||
0x03, 0x62, 0xed, 0xd8, 0x86, 0xd3, 0xc1, 0x19, 0x42, 0x16, 0x34, 0x68, 0x7c, 0xed, 0x45, 0x34,
|
||||
0xb4, 0xaa, 0xb6, 0xe1, 0x34, 0x71, 0x0e, 0x87, 0xa7, 0xd0, 0xce, 0xda, 0xbd, 0xa7, 0x5c, 0xa0,
|
||||
0x57, 0xd0, 0xcc, 0xde, 0xe2, 0x96, 0x61, 0x57, 0x9d, 0xf6, 0xd1, 0x93, 0x51, 0xe8, 0x8f, 0x4a,
|
||||
0xac, 0xb2, 0x96, 0x45, 0xda, 0x1b, 0xf3, 0xcb, 0xdd, 0xa0, 0x32, 0x7c, 0x30, 0x61, 0x5f, 0x66,
|
||||
0x4d, 0xe2, 0x39, 0x3b, 0x4b, 0xd6, 0x71, 0xe0, 0x09, 0x12, 0x22, 0x04, 0x66, 0xec, 0x2d, 0x89,
|
||||
0xa2, 0xdf, 0xc2, 0xca, 0x46, 0xcf, 0xc1, 0x14, 0xe9, 0x4a, 0x33, 0xdc, 0x3d, 0x3a, 0x7c, 0x1c,
|
||||
0xa9, 0x28, 0x4f, 0x57, 0x04, 0xab, 0x1c, 0x59, 0xcf, 0xe9, 0x2d, 0x51, 0xa4, 0xab, 0x58, 0xd9,
|
||||
0xc8, 0x86, 0xf6, 0x8a, 0x24, 0x4b, 0xca, 0x35, 0x4b, 0xd3, 0x36, 0x9c, 0x2e, 0x2e, 0xbb, 0xd0,
|
||||
0x53, 0x80, 0x25, 0x0b, 0xe9, 0x9c, 0x92, 0x70, 0xc6, 0xad, 0x9a, 0xaa, 0x6d, 0xe5, 0x9e, 0xa9,
|
||||
0x5c, 0x46, 0x48, 0x22, 0x22, 0x48, 0x68, 0xd5, 0xf5, 0x32, 0x32, 0x88, 0x9c, 0xc7, 0x35, 0x35,
|
||||
0x64, 0xc4, 0xdd, 0xdd, 0x6e, 0x06, 0x80, 0xbd, 0x9b, 0x89, 0xf6, 0x16, 0x6b, 0x43, 0xcf, 0x60,
|
||||
0x37, 0x66, 0xb3, 0x32, 0x8f, 0xa6, 0x7a, 0xaa, 0x1b, 0xb3, 0x4f, 0x25, 0x26, 0xa5, 0x0b, 0xb6,
|
||||
0xfe, 0xed, 0x82, 0x3d, 0x68, 0x72, 0x72, 0xb5, 0x26, 0x71, 0x40, 0x2c, 0x50, 0xcc, 0x0b, 0x8c,
|
||||
0x06, 0xd0, 0x2e, 0xe6, 0x8a, 0xb9, 0xd5, 0xb6, 0x0d, 0xa7, 0x86, 0x8b, 0x51, 0x3f, 0x70, 0xf4,
|
||||
0xb9, 0x94, 0xe0, 0xa7, 0x56, 0xc7, 0x36, 0x1c, 0xd3, 0x7d, 0x2b, 0x1b, 0x7c, 0xdf, 0x0c, 0x8e,
|
||||
0xff, 0x43, 0x93, 0xa3, 0xe9, 0x05, 0x4b, 0xc4, 0xe4, 0xe4, 0xf1, 0x75, 0x37, 0x45, 0x63, 0x00,
|
||||
0x3f, 0x62, 0xc1, 0xe5, 0x4c, 0x9d, 0xa4, 0x2b, 0xbb, 0xbb, 0x7b, 0xdb, 0xcd, 0xa0, 0x83, 0xbd,
|
||||
0x1b, 0x57, 0x06, 0xa6, 0xf4, 0x96, 0xe0, 0x96, 0x9f, 0x9b, 0x72, 0x49, 0x3c, 0x5d, 0x46, 0x34,
|
||||
0xbe, 0x9c, 0x09, 0x2f, 0x59, 0x10, 0x61, 0xed, 0x2b, 0x1d, 0x74, 0x33, 0xef, 0x99, 0x72, 0xca,
|
||||
0x83, 0x46, 0x2c, 0xf0, 0xa2, 0xd9, 0x3c, 0xf2, 0x16, 0xdc, 0xfa, 0xd5, 0x50, 0x17, 0x05, 0xe5,
|
||||
0x3b, 0x95, 0xae, 0x4c, 0x62, 0x5f, 0x0d, 0xa8, 0xbf, 0x63, 0xeb, 0x58, 0x70, 0x74, 0x00, 0xb5,
|
||||
0x39, 0x8d, 0x08, 0x57, 0xc2, 0xaa, 0x61, 0x0d, 0xe4, 0x43, 0x21, 0x4d, 0xd4, 0x5a, 0x29, 0xe1,
|
||||
0x4a, 0x60, 0x35, 0x5c, 0x76, 0xa9, 0xed, 0xea, 0xde, 0x5c, 0x69, 0xaa, 0x86, 0x0b, 0x5c, 0x96,
|
||||
0x85, 0xa9, 0x42, 0x85, 0x2c, 0x0e, 0xa0, 0xe6, 0xa7, 0x82, 0xe4, 0x52, 0xd2, 0xe0, 0x8f, 0x4b,
|
||||
0xd5, 0xff, 0xba, 0x54, 0x0f, 0x9a, 0xfa, 0xe7, 0x4d, 0x4e, 0xd4, 0xcc, 0x1d, 0x5c, 0xe0, 0xe1,
|
||||
0x47, 0x68, 0xe9, 0x29, 0xa6, 0x44, 0x20, 0x07, 0xea, 0x81, 0x02, 0xd9, 0x6f, 0x03, 0xf9, 0xdb,
|
||||
0x74, 0x38, 0x53, 0x46, 0x16, 0x97, 0xf4, 0x82, 0x84, 0xc8, 0x5f, 0xa5, 0x06, 0xab, 0xe2, 0x1c,
|
||||
0xba, 0x07, 0xf7, 0x3f, 0xfa, 0x95, 0xfb, 0x6d, 0xdf, 0xf8, 0xb6, 0xed, 0x1b, 0x0f, 0xdb, 0x7e,
|
||||
0xe5, 0xee, 0x67, 0xdf, 0xf0, 0xeb, 0xea, 0x96, 0xc7, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9a,
|
||||
0x4b, 0x16, 0x44, 0xcd, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -31,14 +31,16 @@ message FileInfoTruncated {
|
||||
int32 modified_ns = 11;
|
||||
uint64 modified_by = 12 [(gogoproto.customtype) = "github.com/syncthing/syncthing/lib/protocol.ShortID", (gogoproto.nullable) = false];
|
||||
bool deleted = 6;
|
||||
bool invalid = 7;
|
||||
bool invalid = 7 [(gogoproto.customname) = "RawInvalid"];
|
||||
bool no_permissions = 8;
|
||||
protocol.Vector version = 9 [(gogoproto.nullable) = false];
|
||||
int64 sequence = 10;
|
||||
int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"];
|
||||
|
||||
// repeated BlockInfo Blocks = 16
|
||||
string symlink_target = 17;
|
||||
|
||||
// see bep.proto
|
||||
uint32 local_flags = 1000;
|
||||
}
|
||||
|
||||
// For each folder and device we keep one of these to track the current
|
||||
|
||||
@@ -11,7 +11,7 @@ package fs
|
||||
import "github.com/syncthing/notify"
|
||||
|
||||
const (
|
||||
subEventMask = notify.FileNotifyChangeFileName | notify.FileNotifyChangeDirName | notify.FileNotifyChangeSize | notify.FileNotifyChangeCreation
|
||||
subEventMask = notify.FileNotifyChangeFileName | notify.FileNotifyChangeDirName | notify.FileNotifyChangeSize | notify.FileNotifyChangeCreation | notify.FileNotifyChangeLastWrite
|
||||
permEventMask = notify.FileNotifyChangeAttributes
|
||||
rmEventMask = notify.FileActionRemoved | notify.FileActionRenamedOldName
|
||||
)
|
||||
|
||||
+7
-2
@@ -206,8 +206,13 @@ func (f *folder) Scan(subdirs []string) error {
|
||||
subdirs: subdirs,
|
||||
err: make(chan error),
|
||||
}
|
||||
f.scanNow <- req
|
||||
return <-req.err
|
||||
|
||||
select {
|
||||
case f.scanNow <- req:
|
||||
return <-req.err
|
||||
case <-f.ctx.Done():
|
||||
return f.ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *folder) Reschedule() {
|
||||
|
||||
@@ -60,7 +60,7 @@ func (f *sendOnlyFolder) pull() bool {
|
||||
|
||||
if ignores.ShouldIgnore(intf.FileName()) {
|
||||
file := intf.(protocol.FileInfo)
|
||||
file.Invalidate(f.shortID)
|
||||
file.SetIgnored(f.shortID)
|
||||
batch = append(batch, file)
|
||||
batchSizeBytes += file.ProtoSize()
|
||||
l.Debugln(f, "Handling ignored file", file)
|
||||
|
||||
+150
-85
@@ -159,6 +159,11 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
scanChan := make(chan string)
|
||||
go f.pullScannerRoutine(scanChan)
|
||||
|
||||
defer func() {
|
||||
close(scanChan)
|
||||
f.setState(FolderIdle)
|
||||
}()
|
||||
|
||||
var changed int
|
||||
tries := 0
|
||||
|
||||
@@ -166,6 +171,13 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
tries++
|
||||
|
||||
changed = f.pullerIteration(curIgnores, ignoresChanged, scanChan)
|
||||
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return false
|
||||
default:
|
||||
}
|
||||
|
||||
l.Debugln(f, "changed", changed)
|
||||
|
||||
if changed == 0 {
|
||||
@@ -189,10 +201,6 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
}
|
||||
}
|
||||
|
||||
f.setState(FolderIdle)
|
||||
|
||||
close(scanChan)
|
||||
|
||||
if changed == 0 {
|
||||
f.prevIgnoreHash = curIgnoreHash
|
||||
return true
|
||||
@@ -248,18 +256,53 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
doneWg.Done()
|
||||
}()
|
||||
|
||||
changed, fileDeletions, dirDeletions, err := f.processNeeded(ignores, dbUpdateChan, copyChan, finisherChan, scanChan)
|
||||
|
||||
// Signal copy and puller routines that we are done with the in data for
|
||||
// this iteration. Wait for them to finish.
|
||||
close(copyChan)
|
||||
copyWg.Wait()
|
||||
close(pullChan)
|
||||
pullWg.Wait()
|
||||
|
||||
// Signal the finisher chan that there will be no more input and wait
|
||||
// for it to finish.
|
||||
close(finisherChan)
|
||||
doneWg.Wait()
|
||||
|
||||
if err == nil {
|
||||
f.processDeletions(ignores, fileDeletions, dirDeletions, dbUpdateChan, scanChan)
|
||||
}
|
||||
|
||||
// Wait for db updates and scan scheduling to complete
|
||||
close(dbUpdateChan)
|
||||
updateWg.Wait()
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
func (f *sendReceiveFolder) processNeeded(ignores *ignore.Matcher, dbUpdateChan chan<- dbUpdateJob, copyChan chan<- copyBlocksState, finisherChan chan<- *sharedPullerState, scanChan chan<- string) (int, map[string]protocol.FileInfo, []protocol.FileInfo, error) {
|
||||
f.model.fmut.RLock()
|
||||
folderFiles := f.model.folderFiles[f.folderID]
|
||||
f.model.fmut.RUnlock()
|
||||
|
||||
changed := 0
|
||||
var processDirectly []protocol.FileInfo
|
||||
var dirDeletions []protocol.FileInfo
|
||||
fileDeletions := map[string]protocol.FileInfo{}
|
||||
buckets := map[string][]protocol.FileInfo{}
|
||||
|
||||
// Iterate the list of items that we need and sort them into piles.
|
||||
// Regular files to pull goes into the file queue, everything else
|
||||
// (directories, symlinks and deletes) goes into the "process directly"
|
||||
// pile.
|
||||
folderFiles.WithNeed(protocol.LocalDeviceID, func(intf db.FileIntf) bool {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return false
|
||||
default:
|
||||
}
|
||||
|
||||
if f.IgnoreDelete && intf.IsDeleted() {
|
||||
l.Debugln(f, "ignore file deletion (config)", intf.FileName())
|
||||
return true
|
||||
@@ -269,7 +312,7 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
|
||||
switch {
|
||||
case ignores.ShouldIgnore(file.Name):
|
||||
file.Invalidate(f.shortID)
|
||||
file.SetIgnored(f.shortID)
|
||||
l.Debugln(f, "Handling ignored file", file)
|
||||
dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate}
|
||||
changed++
|
||||
@@ -279,7 +322,23 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
changed++
|
||||
|
||||
case file.IsDeleted():
|
||||
processDirectly = append(processDirectly, file)
|
||||
if file.IsDirectory() {
|
||||
// Perform directory deletions at the end, as we may have
|
||||
// files to delete inside them before we get to that point.
|
||||
dirDeletions = append(dirDeletions, file)
|
||||
} else {
|
||||
fileDeletions[file.Name] = file
|
||||
df, ok := f.model.CurrentFolderFile(f.folderID, file.Name)
|
||||
// Local file can be already deleted, but with a lower version
|
||||
// number, hence the deletion coming in again as part of
|
||||
// WithNeed, furthermore, the file can simply be of the wrong
|
||||
// type if we haven't yet managed to pull it.
|
||||
if ok && !df.IsDeleted() && !df.IsSymlink() && !df.IsDirectory() && !df.IsInvalid() {
|
||||
// Put files into buckets per first hash
|
||||
key := string(df.Blocks[0].Hash)
|
||||
buckets[key] = append(buckets[key], df)
|
||||
}
|
||||
}
|
||||
changed++
|
||||
|
||||
case file.Type == protocol.FileInfoTypeFile:
|
||||
@@ -297,13 +356,14 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
l.Debugln(f, "Needed file is unavailable", file)
|
||||
|
||||
case runtime.GOOS == "windows" && file.IsSymlink():
|
||||
file.Invalidate(f.shortID)
|
||||
file.SetUnsupported(f.shortID)
|
||||
l.Debugln(f, "Invalidating symlink (unsupported)", file.Name)
|
||||
dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate}
|
||||
changed++
|
||||
|
||||
default:
|
||||
// Directories, symlinks
|
||||
l.Debugln(f, "to be processed directly", file)
|
||||
processDirectly = append(processDirectly, file)
|
||||
changed++
|
||||
}
|
||||
@@ -311,6 +371,12 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
return true
|
||||
})
|
||||
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return changed, nil, nil, f.ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Sort the "process directly" pile by number of path components. This
|
||||
// ensures that we handle parents before children.
|
||||
|
||||
@@ -318,39 +384,18 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan
|
||||
|
||||
// Process the list.
|
||||
|
||||
fileDeletions := map[string]protocol.FileInfo{}
|
||||
dirDeletions := []protocol.FileInfo{}
|
||||
buckets := map[string][]protocol.FileInfo{}
|
||||
|
||||
for _, fi := range processDirectly {
|
||||
// Verify that the thing we are handling lives inside a directory,
|
||||
// and not a symlink or empty space.
|
||||
if err := osutil.TraversesSymlink(f.fs, filepath.Dir(fi.Name)); err != nil {
|
||||
f.newError("traverses d", fi.Name, err)
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return changed, fileDeletions, dirDeletions, f.ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
if !f.checkParent(fi.Name, scanChan) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case fi.IsDeleted():
|
||||
// A deleted file, directory or symlink
|
||||
if fi.IsDirectory() {
|
||||
// Perform directory deletions at the end, as we may have
|
||||
// files to delete inside them before we get to that point.
|
||||
dirDeletions = append(dirDeletions, fi)
|
||||
} else {
|
||||
fileDeletions[fi.Name] = fi
|
||||
df, ok := f.model.CurrentFolderFile(f.folderID, fi.Name)
|
||||
// Local file can be already deleted, but with a lower version
|
||||
// number, hence the deletion coming in again as part of
|
||||
// WithNeed, furthermore, the file can simply be of the wrong
|
||||
// type if we haven't yet managed to pull it.
|
||||
if ok && !df.IsDeleted() && !df.IsSymlink() && !df.IsDirectory() && !df.IsInvalid() {
|
||||
// Put files into buckets per first hash
|
||||
key := string(df.Blocks[0].Hash)
|
||||
buckets[key] = append(buckets[key], df)
|
||||
}
|
||||
}
|
||||
|
||||
case fi.IsDirectory() && !fi.IsSymlink():
|
||||
l.Debugln(f, "Handling directory", fi.Name)
|
||||
f.handleDir(fi, dbUpdateChan)
|
||||
@@ -389,8 +434,7 @@ nextFile:
|
||||
for {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
// Stop processing files if the puller has been told to stop.
|
||||
break nextFile
|
||||
return changed, fileDeletions, dirDeletions, f.ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -413,33 +457,10 @@ nextFile:
|
||||
continue
|
||||
}
|
||||
|
||||
dirName := filepath.Dir(fi.Name)
|
||||
|
||||
// Verify that the thing we are handling lives inside a directory,
|
||||
// and not a symlink or empty space.
|
||||
if err := osutil.TraversesSymlink(f.fs, dirName); err != nil {
|
||||
f.newError("traverses q", fi.Name, err)
|
||||
if !f.checkParent(fi.Name, scanChan) {
|
||||
continue
|
||||
}
|
||||
|
||||
// issues #114 and #4475: This works around a race condition
|
||||
// between two devices, when one device removes a directory and the
|
||||
// other creates a file in it. However that happens, we end up with
|
||||
// a directory for "foo" with the delete bit, but a file "foo/bar"
|
||||
// that we want to sync. We never create the directory, and hence
|
||||
// fail to create the file and end up looping forever on it. This
|
||||
// breaks that by creating the directory and scheduling a scan,
|
||||
// where it will be found and the delete bit on it removed. The
|
||||
// user can then clean up as they like...
|
||||
if _, err := f.fs.Lstat(dirName); fs.IsNotExist(err) {
|
||||
l.Debugln("%v resurrecting parent directory of %v", f, fi.Name)
|
||||
if err := f.fs.MkdirAll(dirName, 0755); err != nil {
|
||||
f.newError("resurrecting parent dir", fi.Name, err)
|
||||
continue
|
||||
}
|
||||
scanChan <- dirName
|
||||
}
|
||||
|
||||
// Check our list of files to be removed for a match, in which case
|
||||
// we can just do a rename instead.
|
||||
key := string(fi.Blocks[0].Hash)
|
||||
@@ -468,35 +489,32 @@ nextFile:
|
||||
f.handleFile(fi, copyChan, finisherChan, dbUpdateChan)
|
||||
}
|
||||
|
||||
// Signal copy and puller routines that we are done with the in data for
|
||||
// this iteration. Wait for them to finish.
|
||||
close(copyChan)
|
||||
copyWg.Wait()
|
||||
close(pullChan)
|
||||
pullWg.Wait()
|
||||
|
||||
// Signal the finisher chan that there will be no more input.
|
||||
close(finisherChan)
|
||||
|
||||
// Wait for the finisherChan to finish.
|
||||
doneWg.Wait()
|
||||
return changed, fileDeletions, dirDeletions, nil
|
||||
}
|
||||
|
||||
func (f *sendReceiveFolder) processDeletions(ignores *ignore.Matcher, fileDeletions map[string]protocol.FileInfo, dirDeletions []protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) {
|
||||
for _, file := range fileDeletions {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
l.Debugln(f, "Deleting file", file.Name)
|
||||
f.deleteFile(file, dbUpdateChan)
|
||||
}
|
||||
|
||||
for i := range dirDeletions {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
dir := dirDeletions[len(dirDeletions)-i-1]
|
||||
l.Debugln(f, "Deleting dir", dir.Name)
|
||||
f.handleDeleteDir(dir, ignores, dbUpdateChan, scanChan)
|
||||
}
|
||||
|
||||
// Wait for db updates and scan scheduling to complete
|
||||
close(dbUpdateChan)
|
||||
updateWg.Wait()
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
// handleDir creates or updates the given directory
|
||||
@@ -592,6 +610,40 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
|
||||
}
|
||||
}
|
||||
|
||||
// checkParent verifies that the thing we are handling lives inside a directory,
|
||||
// and not a symlink or regular file. It also resurrects missing parent dirs.
|
||||
func (f *sendReceiveFolder) checkParent(file string, scanChan chan<- string) bool {
|
||||
parent := filepath.Dir(file)
|
||||
|
||||
if err := osutil.TraversesSymlink(f.fs, parent); err != nil {
|
||||
f.newError("traverses q", file, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// issues #114 and #4475: This works around a race condition
|
||||
// between two devices, when one device removes a directory and the
|
||||
// other creates a file in it. However that happens, we end up with
|
||||
// a directory for "foo" with the delete bit, but a file "foo/bar"
|
||||
// that we want to sync. We never create the directory, and hence
|
||||
// fail to create the file and end up looping forever on it. This
|
||||
// breaks that by creating the directory and scheduling a scan,
|
||||
// where it will be found and the delete bit on it removed. The
|
||||
// user can then clean up as they like...
|
||||
// This can also occur if an entire tree structure was deleted, but only
|
||||
// a leave has been scanned.
|
||||
if _, err := f.fs.Lstat(parent); !fs.IsNotExist(err) {
|
||||
l.Debugf("%v parent not missing %v", f, file)
|
||||
return true
|
||||
}
|
||||
l.Debugf("%v resurrecting parent directory of %v", f, file)
|
||||
if err := f.fs.MkdirAll(parent, 0755); err != nil {
|
||||
f.newError("resurrecting parent dir", file, err)
|
||||
return false
|
||||
}
|
||||
scanChan <- parent
|
||||
return true
|
||||
}
|
||||
|
||||
// handleSymlink creates or updates the given symlink
|
||||
func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob) {
|
||||
// Used in the defer closure below, updated by the function body. Take
|
||||
@@ -675,9 +727,7 @@ func (f *sendReceiveFolder) handleDeleteDir(file protocol.FileInfo, ignores *ign
|
||||
})
|
||||
}()
|
||||
|
||||
err = f.deleteDir(file.Name, ignores, scanChan)
|
||||
|
||||
if err != nil {
|
||||
if err = f.deleteDir(file.Name, ignores, scanChan); err != nil {
|
||||
f.newError("delete dir", file.Name, err)
|
||||
return
|
||||
}
|
||||
@@ -1097,7 +1147,7 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch
|
||||
if len(hashesToFind) > 0 {
|
||||
file, err = f.fs.Open(state.file.Name)
|
||||
if err == nil {
|
||||
weakHashFinder, err = weakhash.NewFinder(file, int(state.file.BlockSize()), hashesToFind)
|
||||
weakHashFinder, err = weakhash.NewFinder(f.ctx, file, int(state.file.BlockSize()), hashesToFind)
|
||||
if err != nil {
|
||||
l.Debugln("weak hasher", err)
|
||||
}
|
||||
@@ -1109,7 +1159,15 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch
|
||||
l.Debugf("not weak hashing %s. not enough changed %.02f < %d", state.file.Name, blocksPercentChanged, f.WeakHashThresholdPct)
|
||||
}
|
||||
|
||||
blocks:
|
||||
for _, block := range state.blocks {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
state.fail("folder stopped", f.ctx.Err())
|
||||
break blocks
|
||||
default:
|
||||
}
|
||||
|
||||
if !f.DisableSparseFiles && state.reused == 0 && block.IsEmpty() {
|
||||
// The block is a block of all zeroes, and we are not reusing
|
||||
// a temp file, so there is no need to do anything with it.
|
||||
@@ -1275,6 +1333,13 @@ func (f *sendReceiveFolder) pullBlock(state pullBlockState, out chan<- *sharedPu
|
||||
var lastError error
|
||||
candidates := f.model.Availability(f.folderID, state.file, state.block)
|
||||
for {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
state.fail("folder stopped", f.ctx.Err())
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Select the least busy device to pull the block from. If we found no
|
||||
// feasible device at all, fail the block (and in the long run, the
|
||||
// file).
|
||||
@@ -1346,9 +1411,9 @@ func (f *sendReceiveFolder) performFinish(ignores *ignore.Matcher, state *shared
|
||||
// to handle that specially.
|
||||
changed := false
|
||||
switch {
|
||||
case !state.hasCurFile:
|
||||
case !state.hasCurFile || state.curFile.Deleted:
|
||||
// The file appeared from nowhere
|
||||
l.Debugln("file exists but not scanned; not finishing:", state.file.Name)
|
||||
l.Debugln("file exists on disk but not in db; not finishing:", state.file.Name)
|
||||
changed = true
|
||||
|
||||
case stat.IsDir() != state.curFile.IsDirectory() || stat.IsSymlink() != state.curFile.IsSymlink():
|
||||
|
||||
+34
-21
@@ -341,22 +341,26 @@ func (m *Model) RemoveFolder(cfg config.FolderConfiguration) {
|
||||
}
|
||||
|
||||
func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration) {
|
||||
// Stop the services running for this folder and wait for them to finish
|
||||
// stopping to prevent races on restart.
|
||||
tokens := m.folderRunnerTokens[cfg.ID]
|
||||
m.fmut.Unlock()
|
||||
for _, id := range tokens {
|
||||
m.RemoveAndWait(id, 0)
|
||||
}
|
||||
m.fmut.Lock()
|
||||
|
||||
// Close connections to affected devices
|
||||
// Must happen before stopping the folder service to abort ongoing
|
||||
// transmissions and thus allow timely service termination.
|
||||
for _, dev := range cfg.Devices {
|
||||
if conn, ok := m.conn[dev.DeviceID]; ok {
|
||||
closeRawConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the services running for this folder and wait for them to finish
|
||||
// stopping to prevent races on restart.
|
||||
tokens := m.folderRunnerTokens[cfg.ID]
|
||||
m.pmut.Unlock()
|
||||
m.fmut.Unlock()
|
||||
for _, id := range tokens {
|
||||
m.RemoveAndWait(id, 0)
|
||||
}
|
||||
m.fmut.Lock()
|
||||
m.pmut.Lock()
|
||||
|
||||
// Clean up our config maps
|
||||
delete(m.folderCfgs, cfg.ID)
|
||||
delete(m.folderFiles, cfg.ID)
|
||||
@@ -862,6 +866,11 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot
|
||||
if !update {
|
||||
files.Drop(deviceID)
|
||||
}
|
||||
for i := range fs {
|
||||
// The local flags should never be transmitted over the wire. Make
|
||||
// sure they look like they weren't.
|
||||
fs[i].LocalFlags = 0
|
||||
}
|
||||
files.Update(deviceID, fs)
|
||||
|
||||
events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{
|
||||
@@ -1389,7 +1398,7 @@ func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem,
|
||||
// The hashes provided part of the request match what we expect to find according
|
||||
// to what we have in the database, yet the content we've read off the filesystem doesn't
|
||||
// Something is fishy, invalidate the file and rescan it.
|
||||
cf.Invalidate(m.shortID)
|
||||
cf.SetMustRescan(m.shortID)
|
||||
|
||||
// Update the index and tell others
|
||||
// The file will temporarily become invalid, which is ok as the content is messed up.
|
||||
@@ -1736,6 +1745,10 @@ func sendIndexTo(prevSequence int64, conn protocol.Connection, folder string, fs
|
||||
|
||||
f = fi.(protocol.FileInfo)
|
||||
|
||||
// Mark the file as invalid if any of the local bad stuff flags are set.
|
||||
f.RawInvalid = f.IsInvalid()
|
||||
f.LocalFlags = 0 // never sent externally
|
||||
|
||||
if dropSymlinks && f.IsSymlink() {
|
||||
// Do not send index entries with symlinks to clients that can't
|
||||
// handle it. Fixes issue #3802. Once both sides are upgraded, a
|
||||
@@ -2064,23 +2077,23 @@ func (m *Model) internalScanFolderSubdirs(ctx context.Context, folder string, su
|
||||
}
|
||||
|
||||
switch {
|
||||
case !f.IsInvalid() && ignores.Match(f.Name).IsIgnored():
|
||||
// File was valid at last pass but has been ignored. Set invalid bit.
|
||||
l.Debugln("setting invalid bit on ignored", f)
|
||||
nf := f.ConvertToInvalidFileInfo(m.id.Short())
|
||||
case !f.IsIgnored() && ignores.Match(f.Name).IsIgnored():
|
||||
// File was not ignored at last pass but has been ignored.
|
||||
l.Debugln("marking file as ignored", f)
|
||||
nf := f.ConvertToIgnoredFileInfo(m.id.Short())
|
||||
batch = append(batch, nf)
|
||||
batchSizeBytes += nf.ProtoSize()
|
||||
changes++
|
||||
|
||||
case f.IsInvalid() && !ignores.Match(f.Name).IsIgnored():
|
||||
case f.IsIgnored() && !ignores.Match(f.Name).IsIgnored():
|
||||
// Successfully scanned items are already un-ignored during
|
||||
// the scan, so check whether it is deleted.
|
||||
fallthrough
|
||||
case !f.IsInvalid() && !f.IsDeleted():
|
||||
// The file is valid and not deleted. Lets check if it's
|
||||
// still here.
|
||||
// Simply stating it wont do as there are tons of corner
|
||||
// cases (e.g. parent dir->simlink, missing permissions)
|
||||
case !f.IsIgnored() && !f.IsDeleted():
|
||||
// The file is not ignored and not deleted. Lets check if
|
||||
// it's still here. Simply stat:ing it wont do as there are
|
||||
// tons of corner cases (e.g. parent dir->symlink, missing
|
||||
// permissions)
|
||||
if !osutil.IsDeleted(mtimefs, f.Name) {
|
||||
return true
|
||||
}
|
||||
@@ -2099,7 +2112,7 @@ func (m *Model) internalScanFolderSubdirs(ctx context.Context, folder string, su
|
||||
// counter makes sure we are in conflict with any
|
||||
// other existing versions, which will be resolved
|
||||
// by the normal pulling mechanisms.
|
||||
if f.IsInvalid() {
|
||||
if f.IsIgnored() {
|
||||
nf.Version = nf.Version.DropOthers(m.shortID)
|
||||
}
|
||||
|
||||
|
||||
+22
-6
@@ -405,14 +405,9 @@ func (f *fakeConnection) DownloadProgress(folder string, updates []protocol.File
|
||||
})
|
||||
}
|
||||
|
||||
func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) {
|
||||
f.mut.Lock()
|
||||
defer f.mut.Unlock()
|
||||
|
||||
func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol.FileInfoType, data []byte, version protocol.Vector) {
|
||||
blockSize := protocol.BlockSize(int64(len(data)))
|
||||
blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true)
|
||||
var version protocol.Vector
|
||||
version = version.Update(f.id.Short())
|
||||
|
||||
if ftype == protocol.FileInfoTypeFile || ftype == protocol.FileInfoTypeDirectory {
|
||||
f.files = append(f.files, protocol.FileInfo{
|
||||
@@ -442,6 +437,27 @@ func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileI
|
||||
}
|
||||
f.fileData[name] = data
|
||||
}
|
||||
func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) {
|
||||
f.mut.Lock()
|
||||
defer f.mut.Unlock()
|
||||
|
||||
var version protocol.Vector
|
||||
version = version.Update(f.id.Short())
|
||||
f.addFileLocked(name, flags, ftype, data, version)
|
||||
}
|
||||
|
||||
func (f *fakeConnection) updateFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) {
|
||||
f.mut.Lock()
|
||||
defer f.mut.Unlock()
|
||||
|
||||
for i, fi := range f.files {
|
||||
if fi.Name == name {
|
||||
f.files = append(f.files[:i], f.files[i+1:]...)
|
||||
f.addFileLocked(name, flags, ftype, data, fi.Version.Update(f.id.Short()))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeConnection) deleteFile(name string) {
|
||||
f.mut.Lock()
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) {
|
||||
fc.indexFn = func(folder string, fs []protocol.FileInfo) {
|
||||
for _, f := range fs {
|
||||
if f.Name == name {
|
||||
if f.Invalid {
|
||||
if f.IsInvalid() {
|
||||
goodIdx <- struct{}{}
|
||||
} else {
|
||||
t.Fatal("Received index with non-invalid temporary file")
|
||||
@@ -266,7 +266,7 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) {
|
||||
}
|
||||
|
||||
// Recreate foo and a file in it with some data
|
||||
fc.addFile("foo", 0755, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.updateFile("foo", 0755, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.addFile("foo/test", 0644, protocol.FileInfoTypeFile, []byte("testtesttest"))
|
||||
fc.sendIndexUpdate()
|
||||
for updates := 0; updates < 1; updates += <-idx {
|
||||
@@ -348,7 +348,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
if _, ok := expected[f.Name]; !ok {
|
||||
t.Errorf("Unexpected file %v was added to index", f.Name)
|
||||
}
|
||||
if !f.Invalid {
|
||||
if !f.IsInvalid() {
|
||||
t.Errorf("File %v wasn't marked as invalid", f.Name)
|
||||
}
|
||||
delete(expected, f.Name)
|
||||
@@ -382,7 +382,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
|
||||
if _, ok := expected[f.Name]; !ok {
|
||||
t.Fatalf("Unexpected file %v was updated in index", f.Name)
|
||||
}
|
||||
if f.Invalid {
|
||||
if f.IsInvalid() {
|
||||
t.Errorf("File %v is still marked as invalid", f.Name)
|
||||
}
|
||||
// The unignored files should only have a local version,
|
||||
@@ -446,10 +446,10 @@ func TestIssue4841(t *testing.T) {
|
||||
|
||||
// Setup file from remote that was ignored locally
|
||||
m.updateLocals(defaultFolderConfig.ID, []protocol.FileInfo{{
|
||||
Name: "foo",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
Invalid: true,
|
||||
Version: protocol.Vector{}.Update(device2.Short()),
|
||||
Name: "foo",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
LocalFlags: protocol.FlagLocalIgnored,
|
||||
Version: protocol.Vector{}.Update(device2.Short()),
|
||||
}})
|
||||
<-received
|
||||
|
||||
@@ -531,6 +531,83 @@ func TestRescanIfHaveInvalidContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParentDeletion(t *testing.T) {
|
||||
m, fc, tmpDir := setupModelWithConnection()
|
||||
defer m.Stop()
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
parent := "foo"
|
||||
child := filepath.Join(parent, "bar")
|
||||
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
|
||||
|
||||
received := make(chan []protocol.FileInfo)
|
||||
fc.addFile(parent, 0777, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.addFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.mut.Lock()
|
||||
fc.indexFn = func(folder string, fs []protocol.FileInfo) {
|
||||
received <- fs
|
||||
return
|
||||
}
|
||||
fc.mut.Unlock()
|
||||
fc.sendIndexUpdate()
|
||||
|
||||
// Get back index from initial setup
|
||||
select {
|
||||
case fs := <-received:
|
||||
if len(fs) != 2 {
|
||||
t.Fatalf("Sent index with %d files, should be 2", len(fs))
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timed out")
|
||||
}
|
||||
|
||||
// Delete parent dir
|
||||
if err := testFs.RemoveAll(parent); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Scan only the child dir (not the parent)
|
||||
if err := m.ScanFolderSubdirs("default", []string{child}); err != nil {
|
||||
t.Fatal("Failed scanning:", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case fs := <-received:
|
||||
if len(fs) != 1 {
|
||||
t.Fatalf("Sent index with %d files, should be 1", len(fs))
|
||||
}
|
||||
if fs[0].Name != child {
|
||||
t.Fatalf(`Sent index with file "%v", should be "%v"`, fs[0].Name, child)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timed out")
|
||||
}
|
||||
|
||||
// Recreate the child dir on the remote
|
||||
fc.updateFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
|
||||
fc.sendIndexUpdate()
|
||||
|
||||
// Wait for the child dir to be recreated and sent to the remote
|
||||
select {
|
||||
case fs := <-received:
|
||||
l.Debugln("sent:", fs)
|
||||
found := false
|
||||
for _, f := range fs {
|
||||
if f.Name == child {
|
||||
if f.Deleted {
|
||||
t.Fatalf(`File "%v" is still deleted`, child)
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf(`File "%v" is missing in index`, child)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("timed out")
|
||||
}
|
||||
}
|
||||
|
||||
func setupModelWithConnection() (*Model, *fakeConnection, string) {
|
||||
tmpDir := createTmpDir()
|
||||
cfg := defaultCfgWrapper.RawCopy()
|
||||
|
||||
+154
-118
@@ -302,13 +302,18 @@ type FileInfo struct {
|
||||
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
|
||||
ModifiedBy ShortID `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3,customtype=ShortID" json:"modified_by"`
|
||||
Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||
Invalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
|
||||
RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
|
||||
NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"`
|
||||
Version Vector `protobuf:"bytes,9,opt,name=version" json:"version"`
|
||||
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||
RawBlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
|
||||
Blocks []BlockInfo `protobuf:"bytes,16,rep,name=Blocks" json:"Blocks"`
|
||||
SymlinkTarget string `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
|
||||
// The local_flags fields stores flags that are relevant to the local
|
||||
// host only. It is not part of the protocol, doesn't get sent or
|
||||
// received (we make sure to zero it), nonetheless we need it on our
|
||||
// struct and to be able to serialize it to/from the database.
|
||||
LocalFlags uint32 `protobuf:"varint,1000,opt,name=local_flags,json=localFlags,proto3" json:"local_flags,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FileInfo) Reset() { *m = FileInfo{} }
|
||||
@@ -835,10 +840,10 @@ func (m *FileInfo) MarshalTo(dAtA []byte) (int, error) {
|
||||
}
|
||||
i++
|
||||
}
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
dAtA[i] = 0x38
|
||||
i++
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
@@ -905,6 +910,13 @@ func (m *FileInfo) MarshalTo(dAtA []byte) (int, error) {
|
||||
i = encodeVarintBep(dAtA, i, uint64(len(m.SymlinkTarget)))
|
||||
i += copy(dAtA[i:], m.SymlinkTarget)
|
||||
}
|
||||
if m.LocalFlags != 0 {
|
||||
dAtA[i] = 0xc0
|
||||
i++
|
||||
dAtA[i] = 0x3e
|
||||
i++
|
||||
i = encodeVarintBep(dAtA, i, uint64(m.LocalFlags))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -1422,7 +1434,7 @@ func (m *FileInfo) ProtoSize() (n int) {
|
||||
if m.Deleted {
|
||||
n += 2
|
||||
}
|
||||
if m.Invalid {
|
||||
if m.RawInvalid {
|
||||
n += 2
|
||||
}
|
||||
if m.NoPermissions {
|
||||
@@ -1452,6 +1464,9 @@ func (m *FileInfo) ProtoSize() (n int) {
|
||||
if l > 0 {
|
||||
n += 2 + l + sovBep(uint64(l))
|
||||
}
|
||||
if m.LocalFlags != 0 {
|
||||
n += 2 + sovBep(uint64(m.LocalFlags))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -2797,7 +2812,7 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
|
||||
m.Deleted = bool(v != 0)
|
||||
case 7:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RawInvalid", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -2814,7 +2829,7 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Invalid = bool(v != 0)
|
||||
m.RawInvalid = bool(v != 0)
|
||||
case 8:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType)
|
||||
@@ -3001,6 +3016,25 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.SymlinkTarget = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 1000:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LocalFlags", wireType)
|
||||
}
|
||||
m.LocalFlags = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowBep
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.LocalFlags |= (uint32(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipBep(dAtA[iNdEx:])
|
||||
@@ -4220,116 +4254,118 @@ var (
|
||||
func init() { proto.RegisterFile("bep.proto", fileDescriptorBep) }
|
||||
|
||||
var fileDescriptorBep = []byte{
|
||||
// 1762 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x8f, 0xdb, 0xc6,
|
||||
0x15, 0x5f, 0x4a, 0x94, 0x44, 0x3d, 0x69, 0x37, 0xdc, 0xb1, 0xbd, 0x65, 0x99, 0x8d, 0x44, 0x2b,
|
||||
0x76, 0xbc, 0x59, 0x24, 0x6b, 0x37, 0x49, 0x5b, 0xb4, 0x68, 0x0b, 0xe8, 0x0f, 0x77, 0x2d, 0x54,
|
||||
0xa6, 0xd4, 0x91, 0xd6, 0xa9, 0x73, 0x28, 0x41, 0x89, 0x23, 0x2d, 0x61, 0x8a, 0xa3, 0x92, 0xd4,
|
||||
0xda, 0xca, 0x47, 0xd0, 0x27, 0xe8, 0x45, 0x40, 0x80, 0x9e, 0x0a, 0xf4, 0x83, 0xf8, 0x98, 0xf6,
|
||||
0xd0, 0x43, 0x0f, 0x46, 0xb3, 0xbd, 0xf4, 0xd8, 0x4f, 0x50, 0x14, 0x9c, 0x21, 0x25, 0x6a, 0xd7,
|
||||
0x0e, 0x7c, 0xc8, 0x89, 0x33, 0xef, 0xfd, 0xe6, 0x0d, 0xdf, 0x6f, 0x7e, 0xef, 0xcd, 0x40, 0x71,
|
||||
0x48, 0x66, 0x27, 0x33, 0x9f, 0x86, 0x14, 0x49, 0xec, 0x33, 0xa2, 0xae, 0xfa, 0xe9, 0xc4, 0x09,
|
||||
0x2f, 0xe6, 0xc3, 0x93, 0x11, 0x9d, 0x3e, 0x9c, 0xd0, 0x09, 0x7d, 0xc8, 0x3c, 0xc3, 0xf9, 0x98,
|
||||
0xcd, 0xd8, 0x84, 0x8d, 0xf8, 0xc2, 0xda, 0x0c, 0x72, 0x8f, 0x89, 0xeb, 0x52, 0x54, 0x85, 0x92,
|
||||
0x4d, 0x2e, 0x9d, 0x11, 0x31, 0x3d, 0x6b, 0x4a, 0x14, 0x41, 0x13, 0x8e, 0x8a, 0x18, 0xb8, 0xc9,
|
||||
0xb0, 0xa6, 0x24, 0x02, 0x8c, 0x5c, 0x87, 0x78, 0x21, 0x07, 0x64, 0x38, 0x80, 0x9b, 0x18, 0xe0,
|
||||
0x3e, 0xec, 0xc5, 0x80, 0x4b, 0xe2, 0x07, 0x0e, 0xf5, 0x94, 0x2c, 0xc3, 0xec, 0x72, 0xeb, 0x53,
|
||||
0x6e, 0xac, 0x05, 0x90, 0x7f, 0x4c, 0x2c, 0x9b, 0xf8, 0xe8, 0x63, 0x10, 0xc3, 0xc5, 0x8c, 0xef,
|
||||
0xb5, 0xf7, 0xd9, 0x9d, 0x93, 0x24, 0x87, 0x93, 0x27, 0x24, 0x08, 0xac, 0x09, 0x19, 0x2c, 0x66,
|
||||
0x04, 0x33, 0x08, 0xfa, 0x0d, 0x94, 0x46, 0x74, 0x3a, 0xf3, 0x49, 0xc0, 0x02, 0x67, 0xd8, 0x8a,
|
||||
0xc3, 0x1b, 0x2b, 0x9a, 0x1b, 0x0c, 0x4e, 0x2f, 0xa8, 0xd5, 0x61, 0xb7, 0xe9, 0xce, 0x83, 0x90,
|
||||
0xf8, 0x4d, 0xea, 0x8d, 0x9d, 0x09, 0x7a, 0x04, 0x85, 0x31, 0x75, 0x6d, 0xe2, 0x07, 0x8a, 0xa0,
|
||||
0x65, 0x8f, 0x4a, 0x9f, 0xc9, 0x9b, 0x60, 0xa7, 0xcc, 0xd1, 0x10, 0x5f, 0xbd, 0xae, 0xee, 0xe0,
|
||||
0x04, 0x56, 0xfb, 0x73, 0x06, 0xf2, 0xdc, 0x83, 0x0e, 0x20, 0xe3, 0xd8, 0x9c, 0xa2, 0x46, 0xfe,
|
||||
0xea, 0x75, 0x35, 0xd3, 0x6e, 0xe1, 0x8c, 0x63, 0xa3, 0xdb, 0x90, 0x73, 0xad, 0x21, 0x71, 0x63,
|
||||
0x72, 0xf8, 0x04, 0xbd, 0x0f, 0x45, 0x9f, 0x58, 0xb6, 0x49, 0x3d, 0x77, 0xc1, 0x28, 0x91, 0xb0,
|
||||
0x14, 0x19, 0xba, 0x9e, 0xbb, 0x40, 0x9f, 0x02, 0x72, 0x26, 0x1e, 0xf5, 0x89, 0x39, 0x23, 0xfe,
|
||||
0xd4, 0x61, 0x7f, 0x1b, 0x28, 0x22, 0x43, 0xed, 0x73, 0x4f, 0x6f, 0xe3, 0x40, 0x1f, 0xc2, 0x6e,
|
||||
0x0c, 0xb7, 0x89, 0x4b, 0x42, 0xa2, 0xe4, 0x18, 0xb2, 0xcc, 0x8d, 0x2d, 0x66, 0x43, 0x8f, 0xe0,
|
||||
0xb6, 0xed, 0x04, 0xd6, 0xd0, 0x25, 0x66, 0x48, 0xa6, 0x33, 0xd3, 0xf1, 0x6c, 0xf2, 0x92, 0x04,
|
||||
0x4a, 0x9e, 0x61, 0x51, 0xec, 0x1b, 0x90, 0xe9, 0xac, 0xcd, 0x3d, 0xe8, 0x00, 0xf2, 0x33, 0x6b,
|
||||
0x1e, 0x10, 0x5b, 0x29, 0x30, 0x4c, 0x3c, 0x8b, 0x58, 0xe2, 0x0a, 0x08, 0x14, 0xf9, 0x3a, 0x4b,
|
||||
0x2d, 0xe6, 0x48, 0x58, 0x8a, 0x61, 0xb5, 0xff, 0x66, 0x20, 0xcf, 0x3d, 0xe8, 0xa3, 0x35, 0x4b,
|
||||
0xe5, 0xc6, 0x41, 0x84, 0xfa, 0xe7, 0xeb, 0xaa, 0xc4, 0x7d, 0xed, 0x56, 0x8a, 0x35, 0x04, 0x62,
|
||||
0x4a, 0x51, 0x6c, 0x8c, 0x0e, 0xa1, 0x68, 0xd9, 0x76, 0x74, 0x7a, 0x24, 0x50, 0xb2, 0x5a, 0xf6,
|
||||
0xa8, 0x88, 0x37, 0x06, 0xf4, 0xf3, 0x6d, 0x35, 0x88, 0xd7, 0xf5, 0xf3, 0x36, 0x19, 0x44, 0x47,
|
||||
0x31, 0x22, 0x7e, 0xac, 0xe0, 0x1c, 0xdb, 0x4f, 0x8a, 0x0c, 0x4c, 0xbf, 0x77, 0xa1, 0x3c, 0xb5,
|
||||
0x5e, 0x9a, 0x01, 0xf9, 0xe3, 0x9c, 0x78, 0x23, 0xc2, 0xe8, 0xca, 0xe2, 0xd2, 0xd4, 0x7a, 0xd9,
|
||||
0x8f, 0x4d, 0xa8, 0x02, 0xe0, 0x78, 0xa1, 0x4f, 0xed, 0xf9, 0x88, 0xf8, 0x31, 0x57, 0x29, 0x0b,
|
||||
0xfa, 0x29, 0x48, 0x8c, 0x6c, 0xd3, 0xb1, 0x15, 0x49, 0x13, 0x8e, 0xc4, 0x86, 0x1a, 0x27, 0x5e,
|
||||
0x60, 0x54, 0xb3, 0xbc, 0x93, 0x21, 0x2e, 0x30, 0x6c, 0xdb, 0x46, 0xbf, 0x02, 0x35, 0x78, 0xee,
|
||||
0x44, 0x07, 0xc5, 0x23, 0x85, 0x0e, 0xf5, 0x4c, 0x9f, 0x4c, 0xe9, 0xa5, 0xe5, 0x06, 0x4a, 0x91,
|
||||
0x6d, 0xa3, 0x44, 0x88, 0x76, 0x0a, 0x80, 0x63, 0x7f, 0xad, 0x0b, 0x39, 0x16, 0x31, 0x3a, 0x45,
|
||||
0x2e, 0xd6, 0xb8, 0x7a, 0xe3, 0x19, 0x3a, 0x81, 0xdc, 0xd8, 0x71, 0x49, 0xa0, 0x64, 0xd8, 0x19,
|
||||
0xa2, 0x94, 0xd2, 0x1d, 0x97, 0xb4, 0xbd, 0x31, 0x8d, 0x4f, 0x91, 0xc3, 0x6a, 0xe7, 0x50, 0x62,
|
||||
0x01, 0xcf, 0x67, 0xb6, 0x15, 0x92, 0x1f, 0x2c, 0xec, 0x5f, 0x45, 0x90, 0x12, 0xcf, 0xfa, 0xd0,
|
||||
0x85, 0xd4, 0xa1, 0x1f, 0xc7, 0xfd, 0x80, 0x57, 0xf7, 0xc1, 0xcd, 0x78, 0xa9, 0x86, 0x80, 0x40,
|
||||
0x0c, 0x9c, 0xaf, 0x09, 0xab, 0xa7, 0x2c, 0x66, 0x63, 0xa4, 0x41, 0xe9, 0x7a, 0x11, 0xed, 0xe2,
|
||||
0xb4, 0x09, 0x7d, 0x00, 0x30, 0xa5, 0xb6, 0x33, 0x76, 0x88, 0x6d, 0x06, 0x4c, 0x00, 0x59, 0x5c,
|
||||
0x4c, 0x2c, 0x7d, 0xa4, 0x44, 0x72, 0x8f, 0x4a, 0xc8, 0x8e, 0x6b, 0x25, 0x99, 0x46, 0x1e, 0xc7,
|
||||
0xbb, 0xb4, 0x5c, 0x27, 0xa9, 0x90, 0x64, 0x1a, 0x75, 0x3d, 0x8f, 0x6e, 0x15, 0xaf, 0xc4, 0x00,
|
||||
0xbb, 0x1e, 0x4d, 0x17, 0xee, 0x23, 0x28, 0x24, 0x5d, 0x31, 0x3a, 0xcf, 0xad, 0x4a, 0x7a, 0x4a,
|
||||
0x46, 0x21, 0x5d, 0xf7, 0x9b, 0x18, 0x86, 0x54, 0x90, 0xd6, 0x52, 0x04, 0xf6, 0xa7, 0xeb, 0x79,
|
||||
0xd4, 0x8b, 0xd7, 0x79, 0x78, 0x81, 0x52, 0xd2, 0x84, 0xa3, 0x1c, 0x5e, 0xa7, 0x66, 0x44, 0xdb,
|
||||
0x6d, 0x00, 0xc3, 0x85, 0x52, 0x66, 0x5a, 0x7c, 0x2f, 0xd1, 0x62, 0xff, 0x82, 0xfa, 0x61, 0xbb,
|
||||
0xb5, 0x59, 0xd1, 0x58, 0xa0, 0x87, 0x00, 0x43, 0x97, 0x8e, 0x9e, 0x9b, 0x8c, 0xd6, 0xdd, 0x28,
|
||||
0x62, 0x43, 0xbe, 0x7a, 0x5d, 0x2d, 0x63, 0xeb, 0x45, 0x23, 0x72, 0xf4, 0x9d, 0xaf, 0x09, 0x2e,
|
||||
0x0e, 0x93, 0x21, 0xfa, 0x09, 0xe4, 0x99, 0x3d, 0x69, 0x0d, 0xb7, 0x36, 0x09, 0x31, 0x7b, 0x4a,
|
||||
0x00, 0x31, 0x30, 0xe2, 0x2a, 0x58, 0x4c, 0x5d, 0xc7, 0x7b, 0x6e, 0x86, 0x96, 0x3f, 0x21, 0xa1,
|
||||
0xb2, 0xcf, 0x6f, 0x88, 0xd8, 0x3a, 0x60, 0xc6, 0x5f, 0x8a, 0x7f, 0xfa, 0xa6, 0xba, 0x53, 0xf3,
|
||||
0xa0, 0xb8, 0x8e, 0x13, 0x69, 0x90, 0x8e, 0xc7, 0x01, 0x09, 0x99, 0x60, 0xb2, 0x38, 0x9e, 0xad,
|
||||
0x65, 0x90, 0x61, 0x0c, 0x70, 0x19, 0x20, 0x10, 0x2f, 0xac, 0xe0, 0x82, 0x49, 0xa3, 0x8c, 0xd9,
|
||||
0x38, 0x2a, 0xfc, 0x17, 0xc4, 0x7a, 0x6e, 0x32, 0x07, 0x17, 0x86, 0x14, 0x19, 0x1e, 0x5b, 0xc1,
|
||||
0x45, 0xbc, 0xdf, 0xaf, 0x21, 0xcf, 0x0f, 0x02, 0x7d, 0x0e, 0xd2, 0x88, 0xce, 0xbd, 0x70, 0x73,
|
||||
0x39, 0xec, 0xa7, 0x7b, 0x0b, 0xf3, 0xc4, 0x99, 0xad, 0x81, 0xb5, 0x53, 0x28, 0xc4, 0x2e, 0x74,
|
||||
0x7f, 0xdd, 0xf8, 0xc4, 0xc6, 0x9d, 0x6b, 0x9c, 0x6f, 0xdf, 0x16, 0x97, 0x96, 0x3b, 0xe7, 0x3f,
|
||||
0x2f, 0x62, 0x3e, 0xa9, 0xfd, 0x4d, 0x80, 0x02, 0x8e, 0xce, 0x39, 0x08, 0x53, 0xf7, 0x4c, 0x6e,
|
||||
0xeb, 0x9e, 0xd9, 0x54, 0x64, 0x66, 0xab, 0x22, 0x93, 0xa2, 0xca, 0xa6, 0x8a, 0x6a, 0xc3, 0x9c,
|
||||
0xf8, 0x46, 0xe6, 0x72, 0x6f, 0x60, 0x2e, 0x9f, 0x62, 0xee, 0x3e, 0xec, 0x8d, 0x7d, 0x3a, 0x65,
|
||||
0x37, 0x09, 0xf5, 0x2d, 0x7f, 0x11, 0x17, 0xc0, 0x6e, 0x64, 0x1d, 0x24, 0xc6, 0x6d, 0x82, 0xa5,
|
||||
0x6d, 0x82, 0x6b, 0x26, 0x48, 0x98, 0x04, 0x33, 0xea, 0x05, 0xe4, 0xad, 0x39, 0x21, 0x10, 0x6d,
|
||||
0x2b, 0xb4, 0x58, 0x46, 0x65, 0xcc, 0xc6, 0xe8, 0x01, 0x88, 0x23, 0x6a, 0xf3, 0x7c, 0xf6, 0xd2,
|
||||
0x02, 0xd3, 0x7d, 0x9f, 0xfa, 0x4d, 0x6a, 0x13, 0xcc, 0x00, 0xb5, 0x19, 0xc8, 0x2d, 0xfa, 0xc2,
|
||||
0x73, 0xa9, 0x65, 0xf7, 0x7c, 0x3a, 0x89, 0xda, 0xfd, 0x5b, 0xdb, 0x56, 0x0b, 0x0a, 0x73, 0xd6,
|
||||
0xd8, 0x92, 0xc6, 0x75, 0x6f, 0xbb, 0xd1, 0x5c, 0x0f, 0xc4, 0xbb, 0x60, 0x52, 0x9d, 0xf1, 0xd2,
|
||||
0xda, 0x3f, 0x04, 0x50, 0xdf, 0x8e, 0x46, 0x6d, 0x28, 0x71, 0xa4, 0x99, 0x7a, 0xe1, 0x1c, 0xbd,
|
||||
0xcb, 0x46, 0xac, 0xc7, 0xc1, 0x7c, 0x3d, 0x7e, 0xe3, 0xf5, 0x98, 0xea, 0x26, 0xd9, 0x77, 0xeb,
|
||||
0x26, 0x0f, 0x60, 0x97, 0x97, 0x77, 0xf2, 0x18, 0x10, 0xb5, 0xec, 0x51, 0xae, 0x91, 0x91, 0x77,
|
||||
0x70, 0x79, 0xc8, 0xcb, 0x8c, 0xd9, 0x6b, 0x79, 0x10, 0x7b, 0x8e, 0x37, 0xa9, 0x55, 0x21, 0xd7,
|
||||
0x74, 0x29, 0x3b, 0xb0, 0xbc, 0x4f, 0xac, 0x80, 0x7a, 0x09, 0x8f, 0x7c, 0x76, 0xfc, 0xf7, 0x0c,
|
||||
0x94, 0x52, 0x0f, 0x35, 0xf4, 0x08, 0xf6, 0x9a, 0x9d, 0xf3, 0xfe, 0x40, 0xc7, 0x66, 0xb3, 0x6b,
|
||||
0x9c, 0xb6, 0xcf, 0xe4, 0x1d, 0xf5, 0x70, 0xb9, 0xd2, 0x94, 0xe9, 0x06, 0xb4, 0xfd, 0x06, 0xab,
|
||||
0x42, 0xae, 0x6d, 0xb4, 0xf4, 0xdf, 0xcb, 0x82, 0x7a, 0x7b, 0xb9, 0xd2, 0xe4, 0x14, 0x90, 0x5f,
|
||||
0x68, 0x9f, 0x40, 0x99, 0x01, 0xcc, 0xf3, 0x5e, 0xab, 0x3e, 0xd0, 0xe5, 0x8c, 0xaa, 0x2e, 0x57,
|
||||
0xda, 0xc1, 0x75, 0x5c, 0xcc, 0xf9, 0x87, 0x50, 0xc0, 0xfa, 0xef, 0xce, 0xf5, 0xfe, 0x40, 0xce,
|
||||
0xaa, 0x07, 0xcb, 0x95, 0x86, 0x52, 0xc0, 0xa4, 0xa4, 0xee, 0x83, 0x84, 0xf5, 0x7e, 0xaf, 0x6b,
|
||||
0xf4, 0x75, 0x59, 0x54, 0x7f, 0xb4, 0x5c, 0x69, 0xb7, 0xb6, 0x50, 0xb1, 0x4a, 0x7f, 0x06, 0xfb,
|
||||
0xad, 0xee, 0x97, 0x46, 0xa7, 0x5b, 0x6f, 0x99, 0x3d, 0xdc, 0x3d, 0xc3, 0x7a, 0xbf, 0x2f, 0xe7,
|
||||
0xd4, 0xea, 0x72, 0xa5, 0xbd, 0x9f, 0xc2, 0xdf, 0x10, 0xdd, 0x07, 0x20, 0xf6, 0xda, 0xc6, 0x99,
|
||||
0x9c, 0x57, 0x6f, 0x2d, 0x57, 0xda, 0x7b, 0x29, 0x68, 0x44, 0x6a, 0x94, 0x71, 0xb3, 0xd3, 0xed,
|
||||
0xeb, 0x72, 0xe1, 0x46, 0xc6, 0x8c, 0xec, 0xe3, 0x3f, 0x00, 0xba, 0xf9, 0x94, 0x45, 0xf7, 0x40,
|
||||
0x34, 0xba, 0x86, 0x2e, 0xef, 0xf0, 0xfc, 0x6f, 0x22, 0x0c, 0xea, 0x11, 0x54, 0x83, 0x6c, 0xe7,
|
||||
0xab, 0x2f, 0x64, 0x41, 0xfd, 0xf1, 0x72, 0xa5, 0xdd, 0xb9, 0x09, 0xea, 0x7c, 0xf5, 0xc5, 0x31,
|
||||
0x85, 0x52, 0x3a, 0x70, 0x0d, 0xa4, 0x27, 0xfa, 0xa0, 0xde, 0xaa, 0x0f, 0xea, 0xf2, 0x0e, 0xff,
|
||||
0xa5, 0xc4, 0xfd, 0x84, 0x84, 0x16, 0x2b, 0xc2, 0x43, 0xc8, 0x19, 0xfa, 0x53, 0x1d, 0xcb, 0x82,
|
||||
0xba, 0xbf, 0x5c, 0x69, 0xbb, 0x09, 0xc0, 0x20, 0x97, 0xc4, 0x47, 0x15, 0xc8, 0xd7, 0x3b, 0x5f,
|
||||
0xd6, 0x9f, 0xf5, 0xe5, 0x8c, 0x8a, 0x96, 0x2b, 0x6d, 0x2f, 0x71, 0xd7, 0xdd, 0x17, 0xd6, 0x22,
|
||||
0x38, 0xfe, 0x9f, 0x00, 0xe5, 0xf4, 0xf5, 0x8d, 0x2a, 0x20, 0x9e, 0xb6, 0x3b, 0x7a, 0xb2, 0x5d,
|
||||
0xda, 0x17, 0x8d, 0xd1, 0x11, 0x14, 0x5b, 0x6d, 0xac, 0x37, 0x07, 0x5d, 0xfc, 0x2c, 0xc9, 0x25,
|
||||
0x0d, 0x6a, 0x39, 0x3e, 0x13, 0xf8, 0x02, 0xfd, 0x02, 0xca, 0xfd, 0x67, 0x4f, 0x3a, 0x6d, 0xe3,
|
||||
0xb7, 0x26, 0x8b, 0x98, 0x51, 0x1f, 0x2c, 0x57, 0xda, 0xdd, 0x2d, 0x30, 0x99, 0xf9, 0x64, 0x64,
|
||||
0x85, 0xc4, 0xee, 0xf3, 0x1b, 0x26, 0x72, 0x4a, 0x02, 0x6a, 0xc2, 0x7e, 0xb2, 0x74, 0xb3, 0x59,
|
||||
0x56, 0xfd, 0x64, 0xb9, 0xd2, 0x3e, 0xfa, 0xde, 0xf5, 0xeb, 0xdd, 0x25, 0x01, 0xdd, 0x83, 0x42,
|
||||
0x1c, 0x24, 0x51, 0x52, 0x7a, 0x69, 0xbc, 0xe0, 0xf8, 0x2f, 0x02, 0x14, 0xd7, 0xed, 0x2a, 0x22,
|
||||
0xdc, 0xe8, 0x9a, 0x3a, 0xc6, 0x5d, 0x9c, 0x30, 0xb0, 0x76, 0x1a, 0x94, 0x0d, 0xd1, 0x5d, 0x28,
|
||||
0x9c, 0xe9, 0x86, 0x8e, 0xdb, 0xcd, 0xa4, 0x30, 0xd6, 0x90, 0x33, 0xe2, 0x11, 0xdf, 0x19, 0xa1,
|
||||
0x8f, 0xa1, 0x6c, 0x74, 0xcd, 0xfe, 0x79, 0xf3, 0x71, 0x92, 0x3a, 0xdb, 0x3f, 0x15, 0xaa, 0x3f,
|
||||
0x1f, 0x5d, 0x30, 0x3e, 0x8f, 0xa3, 0x1a, 0x7a, 0x5a, 0xef, 0xb4, 0x5b, 0x1c, 0x9a, 0x55, 0x95,
|
||||
0xe5, 0x4a, 0xbb, 0xbd, 0x86, 0xb6, 0xf9, 0x3b, 0x26, 0xc2, 0x1e, 0xdb, 0x50, 0xf9, 0xfe, 0xc6,
|
||||
0x84, 0x34, 0xc8, 0xd7, 0x7b, 0x3d, 0xdd, 0x68, 0x25, 0x7f, 0xbf, 0xf1, 0xd5, 0x67, 0x33, 0xe2,
|
||||
0xd9, 0x11, 0xe2, 0xb4, 0x8b, 0xcf, 0xf4, 0x41, 0xf2, 0xf3, 0x1b, 0xc4, 0x29, 0x8d, 0xae, 0xf7,
|
||||
0xc6, 0xe1, 0xab, 0xef, 0x2a, 0x3b, 0xdf, 0x7e, 0x57, 0xd9, 0x79, 0x75, 0x55, 0x11, 0xbe, 0xbd,
|
||||
0xaa, 0x08, 0xff, 0xba, 0xaa, 0xec, 0xfc, 0xe7, 0xaa, 0x22, 0x7c, 0xf3, 0xef, 0x8a, 0x30, 0xcc,
|
||||
0xb3, 0x46, 0xf6, 0xf9, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x17, 0x2b, 0x62, 0xdd, 0x0e,
|
||||
0x00, 0x00,
|
||||
// 1793 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x73, 0xdb, 0xc6,
|
||||
0x15, 0x17, 0x48, 0xf0, 0xdf, 0x23, 0xa5, 0x40, 0x6b, 0x5b, 0x45, 0x11, 0x85, 0x84, 0x19, 0x3b,
|
||||
0x56, 0x34, 0x89, 0xec, 0x26, 0x69, 0x3b, 0xed, 0xb4, 0x9d, 0xe1, 0x1f, 0x48, 0xe6, 0x54, 0x26,
|
||||
0xd9, 0x25, 0xe5, 0xd4, 0x39, 0x14, 0x03, 0x12, 0x4b, 0x0a, 0x63, 0x10, 0xcb, 0x02, 0xa0, 0x64,
|
||||
0xe6, 0x23, 0xf0, 0x13, 0xf4, 0xc2, 0x99, 0xcc, 0xf4, 0xd4, 0x6f, 0xe2, 0x63, 0xda, 0x43, 0x0f,
|
||||
0x3d, 0x68, 0x1a, 0xf5, 0x92, 0x63, 0x3f, 0x41, 0xa7, 0xb3, 0xbb, 0x00, 0x09, 0x4a, 0x76, 0xc6,
|
||||
0x87, 0x9c, 0xb8, 0xfb, 0xde, 0x6f, 0xdf, 0xf2, 0xfd, 0xde, 0xef, 0xbd, 0x05, 0x14, 0x06, 0x64,
|
||||
0x7a, 0x34, 0xf5, 0x69, 0x48, 0x51, 0x9e, 0xff, 0x0c, 0xa9, 0xab, 0x7d, 0x3a, 0x76, 0xc2, 0xf3,
|
||||
0xd9, 0xe0, 0x68, 0x48, 0x27, 0x8f, 0xc7, 0x74, 0x4c, 0x1f, 0x73, 0xcf, 0x60, 0x36, 0xe2, 0x3b,
|
||||
0xbe, 0xe1, 0x2b, 0x71, 0xb0, 0x3a, 0x85, 0xcc, 0x53, 0xe2, 0xba, 0x14, 0x55, 0xa0, 0x68, 0x93,
|
||||
0x0b, 0x67, 0x48, 0x4c, 0xcf, 0x9a, 0x10, 0x55, 0xd2, 0xa5, 0x83, 0x02, 0x06, 0x61, 0x6a, 0x5b,
|
||||
0x13, 0xc2, 0x00, 0x43, 0xd7, 0x21, 0x5e, 0x28, 0x00, 0x29, 0x01, 0x10, 0x26, 0x0e, 0x78, 0x08,
|
||||
0x3b, 0x11, 0xe0, 0x82, 0xf8, 0x81, 0x43, 0x3d, 0x35, 0xcd, 0x31, 0xdb, 0xc2, 0xfa, 0x5c, 0x18,
|
||||
0xab, 0x01, 0x64, 0x9f, 0x12, 0xcb, 0x26, 0x3e, 0xfa, 0x18, 0xe4, 0x70, 0x3e, 0x15, 0x77, 0xed,
|
||||
0x7c, 0x76, 0xef, 0x28, 0xce, 0xe1, 0xe8, 0x19, 0x09, 0x02, 0x6b, 0x4c, 0xfa, 0xf3, 0x29, 0xc1,
|
||||
0x1c, 0x82, 0x7e, 0x07, 0xc5, 0x21, 0x9d, 0x4c, 0x7d, 0x12, 0xf0, 0xc0, 0x29, 0x7e, 0x62, 0xff,
|
||||
0xd6, 0x89, 0xc6, 0x1a, 0x83, 0x93, 0x07, 0xaa, 0x35, 0xd8, 0x6e, 0xb8, 0xb3, 0x20, 0x24, 0x7e,
|
||||
0x83, 0x7a, 0x23, 0x67, 0x8c, 0x9e, 0x40, 0x6e, 0x44, 0x5d, 0x9b, 0xf8, 0x81, 0x2a, 0xe9, 0xe9,
|
||||
0x83, 0xe2, 0x67, 0xca, 0x3a, 0xd8, 0x31, 0x77, 0xd4, 0xe5, 0xd7, 0x57, 0x95, 0x2d, 0x1c, 0xc3,
|
||||
0xaa, 0x7f, 0x4d, 0x41, 0x56, 0x78, 0xd0, 0x1e, 0xa4, 0x1c, 0x5b, 0x50, 0x54, 0xcf, 0x5e, 0x5f,
|
||||
0x55, 0x52, 0xad, 0x26, 0x4e, 0x39, 0x36, 0xba, 0x0b, 0x19, 0xd7, 0x1a, 0x10, 0x37, 0x22, 0x47,
|
||||
0x6c, 0xd0, 0xfb, 0x50, 0xf0, 0x89, 0x65, 0x9b, 0xd4, 0x73, 0xe7, 0x9c, 0x92, 0x3c, 0xce, 0x33,
|
||||
0x43, 0xc7, 0x73, 0xe7, 0xe8, 0x53, 0x40, 0xce, 0xd8, 0xa3, 0x3e, 0x31, 0xa7, 0xc4, 0x9f, 0x38,
|
||||
0xfc, 0xdf, 0x06, 0xaa, 0xcc, 0x51, 0xbb, 0xc2, 0xd3, 0x5d, 0x3b, 0xd0, 0x87, 0xb0, 0x1d, 0xc1,
|
||||
0x6d, 0xe2, 0x92, 0x90, 0xa8, 0x19, 0x8e, 0x2c, 0x09, 0x63, 0x93, 0xdb, 0xd0, 0x13, 0xb8, 0x6b,
|
||||
0x3b, 0x81, 0x35, 0x70, 0x89, 0x19, 0x92, 0xc9, 0xd4, 0x74, 0x3c, 0x9b, 0xbc, 0x22, 0x81, 0x9a,
|
||||
0xe5, 0x58, 0x14, 0xf9, 0xfa, 0x64, 0x32, 0x6d, 0x09, 0x0f, 0xda, 0x83, 0xec, 0xd4, 0x9a, 0x05,
|
||||
0xc4, 0x56, 0x73, 0x1c, 0x13, 0xed, 0x18, 0x4b, 0x42, 0x01, 0x81, 0xaa, 0xdc, 0x64, 0xa9, 0xc9,
|
||||
0x1d, 0x31, 0x4b, 0x11, 0xac, 0xfa, 0xdf, 0x14, 0x64, 0x85, 0x07, 0x7d, 0xb4, 0x62, 0xa9, 0x54,
|
||||
0xdf, 0x63, 0xa8, 0x7f, 0x5d, 0x55, 0xf2, 0xc2, 0xd7, 0x6a, 0x26, 0x58, 0x43, 0x20, 0x27, 0x14,
|
||||
0xc5, 0xd7, 0x68, 0x1f, 0x0a, 0x96, 0x6d, 0xb3, 0xea, 0x91, 0x40, 0x4d, 0xeb, 0xe9, 0x83, 0x02,
|
||||
0x5e, 0x1b, 0xd0, 0x2f, 0x37, 0xd5, 0x20, 0xdf, 0xd4, 0xcf, 0xdb, 0x64, 0xc0, 0x4a, 0x31, 0x24,
|
||||
0x7e, 0xa4, 0xe0, 0x0c, 0xbf, 0x2f, 0xcf, 0x0c, 0x5c, 0xbf, 0xf7, 0xa1, 0x34, 0xb1, 0x5e, 0x99,
|
||||
0x01, 0xf9, 0xf3, 0x8c, 0x78, 0x43, 0xc2, 0xe9, 0x4a, 0xe3, 0xe2, 0xc4, 0x7a, 0xd5, 0x8b, 0x4c,
|
||||
0xa8, 0x0c, 0xe0, 0x78, 0xa1, 0x4f, 0xed, 0xd9, 0x90, 0xf8, 0x11, 0x57, 0x09, 0x0b, 0xfa, 0x39,
|
||||
0xe4, 0x39, 0xd9, 0xa6, 0x63, 0xab, 0x79, 0x5d, 0x3a, 0x90, 0xeb, 0x5a, 0x94, 0x78, 0x8e, 0x53,
|
||||
0xcd, 0xf3, 0x8e, 0x97, 0x38, 0xc7, 0xb1, 0x2d, 0x1b, 0xfd, 0x06, 0xb4, 0xe0, 0xa5, 0xc3, 0x0a,
|
||||
0x25, 0x22, 0x85, 0x0e, 0xf5, 0x4c, 0x9f, 0x4c, 0xe8, 0x85, 0xe5, 0x06, 0x6a, 0x81, 0x5f, 0xa3,
|
||||
0x32, 0x44, 0x2b, 0x01, 0xc0, 0x91, 0xbf, 0xda, 0x81, 0x0c, 0x8f, 0xc8, 0xaa, 0x28, 0xc4, 0x1a,
|
||||
0x75, 0x6f, 0xb4, 0x43, 0x47, 0x90, 0x19, 0x39, 0x2e, 0x09, 0xd4, 0x14, 0xaf, 0x21, 0x4a, 0x28,
|
||||
0xdd, 0x71, 0x49, 0xcb, 0x1b, 0xd1, 0xa8, 0x8a, 0x02, 0x56, 0x3d, 0x83, 0x22, 0x0f, 0x78, 0x36,
|
||||
0xb5, 0xad, 0x90, 0xfc, 0x68, 0x61, 0xaf, 0x64, 0xc8, 0xc7, 0x9e, 0x55, 0xd1, 0xa5, 0x44, 0xd1,
|
||||
0x0f, 0xa3, 0x79, 0x20, 0xba, 0x7b, 0xef, 0x76, 0xbc, 0xc4, 0x40, 0x40, 0x20, 0x07, 0xce, 0xd7,
|
||||
0x84, 0xf7, 0x53, 0x1a, 0xf3, 0x35, 0xd2, 0xa1, 0x78, 0xb3, 0x89, 0xb6, 0x71, 0xd2, 0x84, 0x3e,
|
||||
0x00, 0x98, 0x50, 0xdb, 0x19, 0x39, 0xc4, 0x36, 0x03, 0x2e, 0x80, 0x34, 0x2e, 0xc4, 0x96, 0x1e,
|
||||
0x52, 0x99, 0xdc, 0x59, 0x0b, 0xd9, 0x51, 0xaf, 0xc4, 0x5b, 0x74, 0x00, 0x39, 0xc7, 0xbb, 0xb0,
|
||||
0x5c, 0x27, 0xea, 0x90, 0xfa, 0xce, 0xf5, 0x55, 0x05, 0xb0, 0x75, 0xd9, 0x12, 0x56, 0x1c, 0xbb,
|
||||
0xd9, 0x14, 0xf4, 0xe8, 0x46, 0x33, 0xe7, 0x79, 0xa8, 0x6d, 0x8f, 0x26, 0x1b, 0xf9, 0x09, 0xe4,
|
||||
0xe2, 0x29, 0xc9, 0xea, 0xbb, 0xd1, 0x59, 0xcf, 0xc9, 0x30, 0xa4, 0xab, 0xf9, 0x13, 0xc1, 0x90,
|
||||
0x06, 0xf9, 0x95, 0x34, 0x81, 0xff, 0xf3, 0xd5, 0x9e, 0xcd, 0xe6, 0x55, 0x5e, 0x5e, 0xa0, 0x16,
|
||||
0x75, 0xe9, 0x20, 0x83, 0x57, 0xa9, 0xb6, 0xd9, 0x75, 0x6b, 0xc0, 0x60, 0xae, 0x96, 0xb8, 0x36,
|
||||
0xdf, 0x8b, 0xb5, 0xd9, 0x3b, 0xa7, 0x7e, 0xd8, 0x6a, 0xae, 0x4f, 0xd4, 0xe7, 0xe8, 0x31, 0xc0,
|
||||
0xc0, 0xa5, 0xc3, 0x97, 0x26, 0xa7, 0x79, 0x9b, 0x45, 0xac, 0x2b, 0xd7, 0x57, 0x95, 0x12, 0xb6,
|
||||
0x2e, 0xeb, 0xcc, 0xd1, 0x73, 0xbe, 0x26, 0xb8, 0x30, 0x88, 0x97, 0xe8, 0x67, 0x90, 0xe5, 0xf6,
|
||||
0x78, 0x54, 0xdc, 0x59, 0x27, 0xc4, 0xed, 0x09, 0x41, 0x44, 0x40, 0xc6, 0x55, 0x30, 0x9f, 0xb8,
|
||||
0x8e, 0xf7, 0xd2, 0x0c, 0x2d, 0x7f, 0x4c, 0x42, 0x75, 0x57, 0xbc, 0x18, 0x91, 0xb5, 0xcf, 0x8d,
|
||||
0xac, 0xae, 0x2e, 0x1d, 0x5a, 0xae, 0x39, 0x72, 0xad, 0x71, 0xa0, 0x7e, 0x9f, 0xe3, 0x85, 0x05,
|
||||
0x6e, 0x3b, 0x66, 0xa6, 0x5f, 0xcb, 0x7f, 0xf9, 0xa6, 0xb2, 0x55, 0xf5, 0xa0, 0xb0, 0xba, 0x89,
|
||||
0xa9, 0x96, 0x8e, 0x46, 0x01, 0x09, 0xb9, 0xc4, 0xd2, 0x38, 0xda, 0xad, 0x84, 0x93, 0xe2, 0x1c,
|
||||
0x09, 0xe1, 0x20, 0x90, 0xcf, 0xad, 0xe0, 0x9c, 0x8b, 0xa9, 0x84, 0xf9, 0x9a, 0x8d, 0x8a, 0x4b,
|
||||
0x62, 0xbd, 0x34, 0xb9, 0x43, 0x48, 0x29, 0xcf, 0x0c, 0x4f, 0xad, 0xe0, 0x3c, 0xba, 0xef, 0xb7,
|
||||
0x90, 0x15, 0xa5, 0x42, 0x9f, 0x43, 0x7e, 0x48, 0x67, 0x5e, 0xb8, 0x7e, 0x4e, 0x76, 0x93, 0xd3,
|
||||
0x88, 0x7b, 0xa2, 0xdc, 0x57, 0xc0, 0xea, 0x31, 0xe4, 0x22, 0x17, 0x7a, 0xb8, 0x1a, 0x95, 0x72,
|
||||
0xfd, 0xde, 0x8d, 0xaa, 0x6c, 0xbe, 0x2f, 0x17, 0x96, 0x3b, 0x13, 0x7f, 0x5e, 0xc6, 0x62, 0x53,
|
||||
0xfd, 0xbb, 0x04, 0x39, 0xcc, 0x94, 0x10, 0x84, 0x89, 0x97, 0x29, 0xb3, 0xf1, 0x32, 0xad, 0x7b,
|
||||
0x38, 0xb5, 0xd1, 0xc3, 0x71, 0x1b, 0xa6, 0x13, 0x6d, 0xb8, 0x66, 0x4e, 0x7e, 0x23, 0x73, 0x99,
|
||||
0x37, 0x30, 0x97, 0x4d, 0x30, 0xf7, 0x10, 0x76, 0x46, 0x3e, 0x9d, 0xf0, 0xb7, 0x87, 0xfa, 0x96,
|
||||
0x3f, 0x8f, 0x06, 0xe5, 0x36, 0xb3, 0xf6, 0x63, 0xe3, 0x26, 0xc1, 0xf9, 0x4d, 0x82, 0xab, 0x26,
|
||||
0xe4, 0x31, 0x09, 0xa6, 0xd4, 0x0b, 0xc8, 0x5b, 0x73, 0x42, 0x20, 0xdb, 0x56, 0x68, 0xf1, 0x8c,
|
||||
0x4a, 0x98, 0xaf, 0xd1, 0x23, 0x90, 0x87, 0xd4, 0x16, 0xf9, 0xec, 0x24, 0x25, 0x68, 0xf8, 0x3e,
|
||||
0xf5, 0x1b, 0xd4, 0x26, 0x98, 0x03, 0xaa, 0x53, 0x50, 0x9a, 0xf4, 0xd2, 0x73, 0xa9, 0x65, 0x77,
|
||||
0x7d, 0x3a, 0x66, 0x0f, 0xc4, 0x5b, 0x07, 0x5d, 0x13, 0x72, 0x33, 0x3e, 0x0a, 0xe3, 0x51, 0xf7,
|
||||
0x60, 0x73, 0x34, 0xdd, 0x0c, 0x24, 0xe6, 0x66, 0xdc, 0xbf, 0xd1, 0xd1, 0xea, 0x3f, 0x25, 0xd0,
|
||||
0xde, 0x8e, 0x46, 0x2d, 0x28, 0x0a, 0xa4, 0x99, 0xf8, 0x26, 0x3a, 0x78, 0x97, 0x8b, 0xf8, 0x54,
|
||||
0x84, 0xd9, 0x6a, 0xfd, 0xc6, 0x07, 0x35, 0x31, 0x6f, 0xd2, 0xef, 0x36, 0x6f, 0x1e, 0xc1, 0xb6,
|
||||
0x18, 0x00, 0xf1, 0xe7, 0x83, 0xac, 0xa7, 0x0f, 0x32, 0xf5, 0x94, 0xb2, 0x85, 0x4b, 0x03, 0xd1,
|
||||
0x66, 0xdc, 0x5e, 0xcd, 0x82, 0xdc, 0x75, 0xbc, 0x71, 0xb5, 0x02, 0x99, 0x86, 0x4b, 0x79, 0xc1,
|
||||
0xb2, 0x3e, 0xb1, 0x02, 0xea, 0xc5, 0x3c, 0x8a, 0xdd, 0xe1, 0x3f, 0x52, 0x50, 0x4c, 0x7c, 0xda,
|
||||
0xa1, 0x27, 0xb0, 0xd3, 0x38, 0x3d, 0xeb, 0xf5, 0x0d, 0x6c, 0x36, 0x3a, 0xed, 0xe3, 0xd6, 0x89,
|
||||
0xb2, 0xa5, 0xed, 0x2f, 0x96, 0xba, 0x3a, 0x59, 0x83, 0x36, 0xbf, 0xda, 0x2a, 0x90, 0x69, 0xb5,
|
||||
0x9b, 0xc6, 0x1f, 0x15, 0x49, 0xbb, 0xbb, 0x58, 0xea, 0x4a, 0x02, 0x28, 0x9e, 0xc0, 0x4f, 0xa0,
|
||||
0xc4, 0x01, 0xe6, 0x59, 0xb7, 0x59, 0xeb, 0x1b, 0x4a, 0x4a, 0xd3, 0x16, 0x4b, 0x7d, 0xef, 0x26,
|
||||
0x2e, 0xe2, 0xfc, 0x43, 0xc8, 0x61, 0xe3, 0x0f, 0x67, 0x46, 0xaf, 0xaf, 0xa4, 0xb5, 0xbd, 0xc5,
|
||||
0x52, 0x47, 0x09, 0x60, 0xdc, 0x52, 0x0f, 0x21, 0x8f, 0x8d, 0x5e, 0xb7, 0xd3, 0xee, 0x19, 0x8a,
|
||||
0xac, 0xfd, 0x64, 0xb1, 0xd4, 0xef, 0x6c, 0xa0, 0x22, 0x95, 0xfe, 0x02, 0x76, 0x9b, 0x9d, 0x2f,
|
||||
0xdb, 0xa7, 0x9d, 0x5a, 0xd3, 0xec, 0xe2, 0xce, 0x09, 0x36, 0x7a, 0x3d, 0x25, 0xa3, 0x55, 0x16,
|
||||
0x4b, 0xfd, 0xfd, 0x04, 0xfe, 0x96, 0xe8, 0x3e, 0x00, 0xb9, 0xdb, 0x6a, 0x9f, 0x28, 0x59, 0xed,
|
||||
0xce, 0x62, 0xa9, 0xbf, 0x97, 0x80, 0x32, 0x52, 0x59, 0xc6, 0x8d, 0xd3, 0x4e, 0xcf, 0x50, 0x72,
|
||||
0xb7, 0x32, 0xe6, 0x64, 0x1f, 0xfe, 0x09, 0xd0, 0xed, 0x8f, 0x5f, 0xf4, 0x00, 0xe4, 0x76, 0xa7,
|
||||
0x6d, 0x28, 0x5b, 0x22, 0xff, 0xdb, 0x88, 0x36, 0xf5, 0x08, 0xaa, 0x42, 0xfa, 0xf4, 0xab, 0x2f,
|
||||
0x14, 0x49, 0xfb, 0xe9, 0x62, 0xa9, 0xdf, 0xbb, 0x0d, 0x3a, 0xfd, 0xea, 0x8b, 0x43, 0x0a, 0xc5,
|
||||
0x64, 0xe0, 0x2a, 0xe4, 0x9f, 0x19, 0xfd, 0x5a, 0xb3, 0xd6, 0xaf, 0x29, 0x5b, 0xe2, 0x2f, 0xc5,
|
||||
0xee, 0x67, 0x24, 0xb4, 0x78, 0x13, 0xee, 0x43, 0xa6, 0x6d, 0x3c, 0x37, 0xb0, 0x22, 0x69, 0xbb,
|
||||
0x8b, 0xa5, 0xbe, 0x1d, 0x03, 0xda, 0xe4, 0x82, 0xf8, 0xa8, 0x0c, 0xd9, 0xda, 0xe9, 0x97, 0xb5,
|
||||
0x17, 0x3d, 0x25, 0xa5, 0xa1, 0xc5, 0x52, 0xdf, 0x89, 0xdd, 0x35, 0xf7, 0xd2, 0x9a, 0x07, 0x87,
|
||||
0xff, 0x93, 0xa0, 0x94, 0x7c, 0xf0, 0x51, 0x19, 0xe4, 0xe3, 0xd6, 0xa9, 0x11, 0x5f, 0x97, 0xf4,
|
||||
0xb1, 0x35, 0x3a, 0x80, 0x42, 0xb3, 0x85, 0x8d, 0x46, 0xbf, 0x83, 0x5f, 0xc4, 0xb9, 0x24, 0x41,
|
||||
0x4d, 0xc7, 0xe7, 0x02, 0x9f, 0xa3, 0x5f, 0x41, 0xa9, 0xf7, 0xe2, 0xd9, 0x69, 0xab, 0xfd, 0x7b,
|
||||
0x93, 0x47, 0x4c, 0x69, 0x8f, 0x16, 0x4b, 0xfd, 0xfe, 0x06, 0x98, 0x4c, 0x7d, 0x32, 0xb4, 0x42,
|
||||
0x62, 0xf7, 0xc4, 0x1b, 0xc4, 0x9c, 0x79, 0x09, 0x35, 0x60, 0x37, 0x3e, 0xba, 0xbe, 0x2c, 0xad,
|
||||
0x7d, 0xb2, 0x58, 0xea, 0x1f, 0xfd, 0xe0, 0xf9, 0xd5, 0xed, 0x79, 0x09, 0x3d, 0x80, 0x5c, 0x14,
|
||||
0x24, 0x56, 0x52, 0xf2, 0x68, 0x74, 0xe0, 0xf0, 0x6f, 0x12, 0x14, 0x56, 0xe3, 0x8a, 0x11, 0xde,
|
||||
0xee, 0x98, 0x06, 0xc6, 0x1d, 0x1c, 0x33, 0xb0, 0x72, 0xb6, 0x29, 0x5f, 0xa2, 0xfb, 0x90, 0x3b,
|
||||
0x31, 0xda, 0x06, 0x6e, 0x35, 0xe2, 0xc6, 0x58, 0x41, 0x4e, 0x88, 0x47, 0x7c, 0x67, 0x88, 0x3e,
|
||||
0x86, 0x52, 0xbb, 0x63, 0xf6, 0xce, 0x1a, 0x4f, 0xe3, 0xd4, 0xf9, 0xfd, 0x89, 0x50, 0xbd, 0xd9,
|
||||
0xf0, 0x9c, 0xf3, 0x79, 0xc8, 0x7a, 0xe8, 0x79, 0xed, 0xb4, 0xd5, 0x14, 0xd0, 0xb4, 0xa6, 0x2e,
|
||||
0x96, 0xfa, 0xdd, 0x15, 0x34, 0xfa, 0xe4, 0x61, 0xd8, 0x43, 0x1b, 0xca, 0x3f, 0x3c, 0x98, 0x90,
|
||||
0x0e, 0xd9, 0x5a, 0xb7, 0x6b, 0xb4, 0x9b, 0xf1, 0xbf, 0x5f, 0xfb, 0x6a, 0xd3, 0x29, 0xf1, 0x6c,
|
||||
0x86, 0x38, 0xee, 0xe0, 0x13, 0xa3, 0x1f, 0xff, 0xf9, 0x35, 0xe2, 0x98, 0xb2, 0x0f, 0x80, 0xfa,
|
||||
0xfe, 0xeb, 0xef, 0xca, 0x5b, 0xdf, 0x7e, 0x57, 0xde, 0x7a, 0x7d, 0x5d, 0x96, 0xbe, 0xbd, 0x2e,
|
||||
0x4b, 0xff, 0xbe, 0x2e, 0x6f, 0x7d, 0x7f, 0x5d, 0x96, 0xbe, 0xf9, 0x4f, 0x59, 0x1a, 0x64, 0xf9,
|
||||
0x20, 0xfb, 0xfc, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x41, 0xd9, 0x3a, 0x0f, 0x0f, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
+19
-14
@@ -93,22 +93,27 @@ message IndexUpdate {
|
||||
|
||||
message FileInfo {
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
string name = 1;
|
||||
FileInfoType type = 2;
|
||||
int64 size = 3;
|
||||
uint32 permissions = 4;
|
||||
int64 modified_s = 5;
|
||||
int32 modified_ns = 11;
|
||||
uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false];
|
||||
bool deleted = 6;
|
||||
bool invalid = 7;
|
||||
bool no_permissions = 8;
|
||||
Vector version = 9 [(gogoproto.nullable) = false];
|
||||
int64 sequence = 10;
|
||||
int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"];
|
||||
|
||||
string name = 1;
|
||||
FileInfoType type = 2;
|
||||
int64 size = 3;
|
||||
uint32 permissions = 4;
|
||||
int64 modified_s = 5;
|
||||
int32 modified_ns = 11;
|
||||
uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false];
|
||||
bool deleted = 6;
|
||||
bool invalid = 7 [(gogoproto.customname) = "RawInvalid"];
|
||||
bool no_permissions = 8;
|
||||
Vector version = 9 [(gogoproto.nullable) = false];
|
||||
int64 sequence = 10;
|
||||
int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"];
|
||||
repeated BlockInfo Blocks = 16 [(gogoproto.nullable) = false];
|
||||
string symlink_target = 17;
|
||||
|
||||
// The local_flags fields stores flags that are relevant to the local
|
||||
// host only. It is not part of the protocol, doesn't get sent or
|
||||
// received (we make sure to zero it), nonetheless we need it on our
|
||||
// struct and to be able to serialize it to/from the database.
|
||||
uint32 local_flags = 1000;
|
||||
}
|
||||
|
||||
enum FileInfoType {
|
||||
|
||||
@@ -28,14 +28,14 @@ func (m Hello) Magic() uint32 {
|
||||
func (f FileInfo) String() string {
|
||||
switch f.Type {
|
||||
case FileInfoTypeDirectory:
|
||||
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, NoPermissions:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.Invalid, f.NoPermissions)
|
||||
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions)
|
||||
case FileInfoTypeFile:
|
||||
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, BlockSize:%d, Blocks:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.RawBlockSize, f.Blocks)
|
||||
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, Blocks:%v}",
|
||||
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize, f.Blocks)
|
||||
case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
|
||||
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, NoPermissions:%v, SymlinkTarget:%q}",
|
||||
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.Invalid, f.NoPermissions, f.SymlinkTarget)
|
||||
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q}",
|
||||
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget)
|
||||
default:
|
||||
panic("mystery file type detected")
|
||||
}
|
||||
@@ -46,13 +46,25 @@ func (f FileInfo) IsDeleted() bool {
|
||||
}
|
||||
|
||||
func (f FileInfo) IsInvalid() bool {
|
||||
return f.Invalid
|
||||
return f.RawInvalid || f.LocalFlags&LocalInvalidFlags != 0
|
||||
}
|
||||
|
||||
func (f FileInfo) IsIgnored() bool {
|
||||
return f.LocalFlags&FlagLocalIgnored != 0
|
||||
}
|
||||
|
||||
func (f FileInfo) MustRescan() bool {
|
||||
return f.LocalFlags&FlagLocalMustRescan != 0
|
||||
}
|
||||
|
||||
func (f FileInfo) IsDirectory() bool {
|
||||
return f.Type == FileInfoTypeDirectory
|
||||
}
|
||||
|
||||
func (f FileInfo) ShouldConflict() bool {
|
||||
return f.LocalFlags&LocalConflictFlags != 0
|
||||
}
|
||||
|
||||
func (f FileInfo) IsSymlink() bool {
|
||||
switch f.Type {
|
||||
case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile:
|
||||
@@ -149,7 +161,14 @@ func (f FileInfo) IsEmpty() bool {
|
||||
// - target
|
||||
// A directory does not have anything specific to check.
|
||||
func (f FileInfo) IsEquivalent(other FileInfo, ignorePerms bool, ignoreBlocks bool) bool {
|
||||
if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.Invalid != other.Invalid {
|
||||
if f.MustRescan() || other.MustRescan() {
|
||||
// These are per definition not equivalent because they don't
|
||||
// represent a valid state, even if both happen to have the
|
||||
// MustRescan bit set.
|
||||
return false
|
||||
}
|
||||
|
||||
if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.IsInvalid() != other.IsInvalid() {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -197,9 +216,23 @@ func BlocksEqual(a, b []BlockInfo) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *FileInfo) Invalidate(invalidatedBy ShortID) {
|
||||
f.Invalid = true
|
||||
f.ModifiedBy = invalidatedBy
|
||||
func (f *FileInfo) SetMustRescan(by ShortID) {
|
||||
f.LocalFlags = FlagLocalMustRescan
|
||||
f.ModifiedBy = by
|
||||
f.Blocks = nil
|
||||
f.Sequence = 0
|
||||
}
|
||||
|
||||
func (f *FileInfo) SetIgnored(by ShortID) {
|
||||
f.LocalFlags = FlagLocalIgnored
|
||||
f.ModifiedBy = by
|
||||
f.Blocks = nil
|
||||
f.Sequence = 0
|
||||
}
|
||||
|
||||
func (f *FileInfo) SetUnsupported(by ShortID) {
|
||||
f.LocalFlags = FlagLocalUnsupported
|
||||
f.ModifiedBy = by
|
||||
f.Blocks = nil
|
||||
f.Sequence = 0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ func TestWinsConflict(t *testing.T) {
|
||||
// The first should always win over the second
|
||||
{{ModifiedS: 42}, {ModifiedS: 41}},
|
||||
{{ModifiedS: 41}, {ModifiedS: 42, Deleted: true}},
|
||||
{{Deleted: true}, {ModifiedS: 10, Invalid: true}},
|
||||
{{Deleted: true}, {ModifiedS: 10, RawInvalid: true}},
|
||||
{{ModifiedS: 41, Version: Vector{[]Counter{{42, 2}, {43, 1}}}}, {ModifiedS: 41, Version: Vector{[]Counter{{42, 1}, {43, 2}}}}},
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,21 @@ const (
|
||||
FlagShareBits = 0x000000ff
|
||||
)
|
||||
|
||||
// FileInfo.LocalFlags flags
|
||||
const (
|
||||
FlagLocalUnsupported = 1 << 0 // The kind is unsupported, e.g. symlinks on Windows
|
||||
FlagLocalIgnored = 1 << 1 // Matches local ignore patterns
|
||||
FlagLocalMustRescan = 1 << 2 // Doesn't match content on disk, must be rechecked fully
|
||||
|
||||
// Flags that should result in the Invalid bit on outgoing updates
|
||||
LocalInvalidFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalMustRescan
|
||||
|
||||
// Flags that should result in a file being in conflict with its
|
||||
// successor, due to us not having an up to date picture of its state on
|
||||
// disk.
|
||||
LocalConflictFlags = FlagLocalUnsupported | FlagLocalIgnored
|
||||
)
|
||||
|
||||
var (
|
||||
ErrClosed = errors.New("connection closed")
|
||||
ErrTimeout = errors.New("read timeout")
|
||||
@@ -535,7 +550,7 @@ func checkFileInfoConsistency(f FileInfo) error {
|
||||
// Directories should have no blocks
|
||||
return errDirectoryHasBlocks
|
||||
|
||||
case !f.Deleted && !f.Invalid && f.Type == FileInfoTypeFile && len(f.Blocks) == 0:
|
||||
case !f.Deleted && !f.IsInvalid() && f.Type == FileInfoTypeFile && len(f.Blocks) == 0:
|
||||
// Non-deleted, non-invalid files should have at least one block
|
||||
return errFileHasNoBlocks
|
||||
}
|
||||
|
||||
@@ -391,6 +391,28 @@ func BenchmarkBlockSize(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalFlagBits(t *testing.T) {
|
||||
var f FileInfo
|
||||
if f.IsIgnored() || f.MustRescan() || f.IsInvalid() {
|
||||
t.Error("file should have no weird bits set by default")
|
||||
}
|
||||
|
||||
f.SetIgnored(42)
|
||||
if !f.IsIgnored() || f.MustRescan() || !f.IsInvalid() {
|
||||
t.Error("file should be ignored and invalid")
|
||||
}
|
||||
|
||||
f.SetMustRescan(42)
|
||||
if f.IsIgnored() || !f.MustRescan() || !f.IsInvalid() {
|
||||
t.Error("file should be must-rescan and invalid")
|
||||
}
|
||||
|
||||
f.SetUnsupported(42)
|
||||
if f.IsIgnored() || f.MustRescan() || !f.IsInvalid() {
|
||||
t.Error("file should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEquivalent(t *testing.T) {
|
||||
b := func(v bool) *bool {
|
||||
return &v
|
||||
@@ -430,8 +452,8 @@ func TestIsEquivalent(t *testing.T) {
|
||||
eq: false,
|
||||
},
|
||||
{
|
||||
a: FileInfo{Invalid: false},
|
||||
b: FileInfo{Invalid: true},
|
||||
a: FileInfo{RawInvalid: false},
|
||||
b: FileInfo{RawInvalid: true},
|
||||
eq: false,
|
||||
},
|
||||
{
|
||||
@@ -445,6 +467,31 @@ func TestIsEquivalent(t *testing.T) {
|
||||
eq: false,
|
||||
},
|
||||
|
||||
// Special handling of local flags and invalidity. "MustRescan"
|
||||
// files are never equivalent to each other. Otherwise, equivalence
|
||||
// is based just on whether the file becomes IsInvalid() or not, not
|
||||
// the specific reason or flag bits.
|
||||
{
|
||||
a: FileInfo{LocalFlags: FlagLocalMustRescan},
|
||||
b: FileInfo{LocalFlags: FlagLocalMustRescan},
|
||||
eq: false,
|
||||
},
|
||||
{
|
||||
a: FileInfo{RawInvalid: true},
|
||||
b: FileInfo{RawInvalid: true},
|
||||
eq: true,
|
||||
},
|
||||
{
|
||||
a: FileInfo{LocalFlags: FlagLocalUnsupported},
|
||||
b: FileInfo{LocalFlags: FlagLocalUnsupported},
|
||||
eq: true,
|
||||
},
|
||||
{
|
||||
a: FileInfo{RawInvalid: true},
|
||||
b: FileInfo{LocalFlags: FlagLocalUnsupported},
|
||||
eq: true,
|
||||
},
|
||||
|
||||
// Difference in blocks is not OK
|
||||
{
|
||||
a: FileInfo{Blocks: []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}},
|
||||
|
||||
+18
-15
@@ -373,11 +373,12 @@ func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileIn
|
||||
if curFile.IsEquivalent(f, w.IgnorePerms, true) {
|
||||
return nil
|
||||
}
|
||||
if curFile.Invalid {
|
||||
// We do not want to override the global version with the file we
|
||||
// currently have. Keeping only our local counter makes sure we are in
|
||||
// conflict with any other existing versions, which will be resolved by
|
||||
// the normal pulling mechanisms.
|
||||
if curFile.ShouldConflict() {
|
||||
// The old file was invalid for whatever reason and probably not
|
||||
// up to date with what was out there in the cluster. Drop all
|
||||
// others from the version vector to indicate that we haven't
|
||||
// taken their version into account, and possibly cause a
|
||||
// conflict.
|
||||
f.Version = f.Version.DropOthers(w.ShortID)
|
||||
}
|
||||
l.Debugln("rescan:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm)
|
||||
@@ -412,11 +413,12 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo,
|
||||
if cf.IsEquivalent(f, w.IgnorePerms, true) {
|
||||
return nil
|
||||
}
|
||||
if cf.Invalid {
|
||||
// We do not want to override the global version with the file we
|
||||
// currently have. Keeping only our local counter makes sure we are in
|
||||
// conflict with any other existing versions, which will be resolved by
|
||||
// the normal pulling mechanisms.
|
||||
if cf.ShouldConflict() {
|
||||
// The old file was invalid for whatever reason and probably not
|
||||
// up to date with what was out there in the cluster. Drop all
|
||||
// others from the version vector to indicate that we haven't
|
||||
// taken their version into account, and possibly cause a
|
||||
// conflict.
|
||||
f.Version = f.Version.DropOthers(w.ShortID)
|
||||
}
|
||||
}
|
||||
@@ -467,11 +469,12 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, dchan chan pro
|
||||
if cf.IsEquivalent(f, w.IgnorePerms, true) {
|
||||
return nil
|
||||
}
|
||||
if cf.Invalid {
|
||||
// We do not want to override the global version with the file we
|
||||
// currently have. Keeping only our local counter makes sure we are in
|
||||
// conflict with any other existing versions, which will be resolved by
|
||||
// the normal pulling mechanisms.
|
||||
if cf.ShouldConflict() {
|
||||
// The old file was invalid for whatever reason and probably not
|
||||
// up to date with what was out there in the cluster. Drop all
|
||||
// others from the version vector to indicate that we haven't
|
||||
// taken their version into account, and possibly cause a
|
||||
// conflict.
|
||||
f.Version = f.Version.DropOthers(w.ShortID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,10 +648,10 @@ func TestIssue4841(t *testing.T) {
|
||||
Hashers: 2,
|
||||
CurrentFiler: fakeCurrentFiler{
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
Invalid: true,
|
||||
Version: protocol.Vector{}.Update(1),
|
||||
Name: "foo",
|
||||
Type: protocol.FileInfoTypeFile,
|
||||
LocalFlags: protocol.FlagLocalIgnored,
|
||||
Version: protocol.Vector{}.Update(1),
|
||||
},
|
||||
},
|
||||
ShortID: protocol.LocalDeviceID.Short(),
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package weakhash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -28,7 +29,7 @@ func BenchmarkFind1MFile(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_, err = Find(fd, []uint32{0, 1, 2}, size)
|
||||
_, err = Find(context.Background(), fd, []uint32{0, 1, 2}, size)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package weakhash
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/chmduquesne/rollinghash/adler32"
|
||||
@@ -23,7 +24,7 @@ const (
|
||||
// Find finds all the blocks of the given size within io.Reader that matches
|
||||
// the hashes provided, and returns a hash -> slice of offsets within reader
|
||||
// map, that produces the same weak hash.
|
||||
func Find(ir io.Reader, hashesToFind []uint32, size int) (map[uint32][]int64, error) {
|
||||
func Find(ctx context.Context, ir io.Reader, hashesToFind []uint32, size int) (map[uint32][]int64, error) {
|
||||
if ir == nil || len(hashesToFind) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -50,6 +51,12 @@ func Find(ir io.Reader, hashesToFind []uint32, size int) (map[uint32][]int64, er
|
||||
var i int64
|
||||
var hash uint32
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
hash = hf.Sum32()
|
||||
if existing, ok := offsets[hash]; ok && len(existing) < maxWeakhashFinderHits {
|
||||
offsets[hash] = append(existing, i)
|
||||
@@ -67,8 +74,8 @@ func Find(ir io.Reader, hashesToFind []uint32, size int) (map[uint32][]int64, er
|
||||
return offsets, nil
|
||||
}
|
||||
|
||||
func NewFinder(ir io.ReadSeeker, size int, hashesToFind []uint32) (*Finder, error) {
|
||||
offsets, err := Find(ir, hashesToFind, size)
|
||||
func NewFinder(ctx context.Context, ir io.ReadSeeker, size int, hashesToFind []uint32) (*Finder, error) {
|
||||
offsets, err := Find(ctx, ir, hashesToFind, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package weakhash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -36,7 +37,7 @@ func TestFinder(t *testing.T) {
|
||||
}
|
||||
|
||||
hashes := []uint32{65143183, 65798547}
|
||||
finder, err := NewFinder(f, 4, hashes)
|
||||
finder, err := NewFinder(context.Background(), f, 4, hashes)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STDISCOSRV" "1" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STRELAYSRV" "1" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 05, 2018" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 21, 2018" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user