Compare commits

...

24 Commits

Author SHA1 Message Date
Jakob Borg
6f0acacbd2 fix(sqlite): actually always insert blocks for local files (fixes #10388) (#10411)
Due to a thinko, this optimisation was wildly incorrect and would read
to lack of block reuse when syncing files.

(We do not insert a blocklist per device, but only a single one. We
can't use the fact of whether the insert happened as a criteria for
inserting blocks.)

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-23 12:46:31 +00:00
Jakob Borg
932b4ce9bd fix(model): don't announce untrusted devices to other devices (fixes #10393) (#10408)
fix(model): don't announce untrusted devices to other devices

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-23 13:16:58 +02:00
mrclmr
b3e3ca7294 build: update GitHub actions (#10399) 2025-09-22 08:50:09 +03:00
Syncthing Release Automation
41b4c5cd5e chore(gui, man, authors): update docs, translations, and contributors 2025-09-22 03:52:07 +00:00
Tommy van der Vorst
eb4eb7524d fix(syncthing): only perform CPU benchmark on startup when logging enabled, and on goroutine (#10398)
Signed-off-by: Tommy van der Vorst <tommy@pixelspark.nl>
Co-authored-by: bt90 <btom1990@googlemail.com>
2025-09-19 07:17:10 +02:00
Tommy van der Vorst
a64c5396e9 fix(db): only perform foreign key checking when a migration was applied (#10397) 2025-09-18 12:22:35 +00:00
Jakob Borg
5595113074 fix(gui): don't fetch usage report preview unnecessarily on GUI load (#10395)
IMHO the logic here was inverted. The only use for the report data is to
show a preview when we ask the user whether they want to participate in
usage reporting. However, the GUI would first load the report data and
then consider whether we wanted to show that dialog or not. Instead,
only load if it we're going to show the dialog.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-16 18:01:17 +02:00
Jakob Borg
ea19ec64bf fix(ur): properly skip zero/empty fields in report (#10394)
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-15 20:35:59 +00:00
Syncthing Release Automation
9de6c5ed69 chore(gui, man, authors): update docs, translations, and contributors 2025-09-15 03:52:28 +00:00
Jakob Borg
d037681fd1 fix: improve conflict detection by tracking previous file hash (fixes #10349) (#10351)
This adds a new field to the file information we keep, the "previous
blocks hash". This is the hash of the file contents as it was in its
previous incarnation. That is, every scan that updates the blocks hash
will move the current hash to the "previous" field.

This enables an addition to the conflict detection algorithm: if the
file to be synced is in conflict with the current file on disk
(version-counter wise), but it indicates that it was based on the
precise contents we have (new.prevBlocksHash == current.blocksHash),
then it's not really a conflict.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-13 16:16:28 +02:00
Jakob Borg
1b0eaa093a chore(policy): increase power & responsibility for maintainers
This adds a rule where a maintainer can merge a PR on their own even in
non-trivial cases, which we (I) already do as-is today, but by breaking
the rules instead of following them. This just codifies that behavior.
If we get to a point where it's no longer necessary, that'd be cool.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-13 15:42:54 +02:00
Jakob Borg
3382ccc3f1 chore(model): slightly deflake TestRecvOnlyRevertOwnID (#10390)
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-12 09:41:47 +00:00
Jakob Borg
9ee208b441 chore(sqlite): use normalised tables for file names and versions (#10383)
This changes the files table to use normalisation for the names and
versions. The idea is that these are often common between all remote
devices, and repeating an integer is more efficient than repeating a
long string. A new benchmark bears this out; for a database with 100k
files shared between 31 devices, with some worst case assumption on
version vector size, the database is reduced in size by 50% and the test
finishes quicker:

    Current:
        db_bench_test.go:322: Total size: 6263.70 MiB
    --- PASS: TestBenchmarkSizeManyFilesRemotes (1084.89s)

    New:
        db_bench_test.go:326: Total size: 3049.95 MiB
    --- PASS: TestBenchmarkSizeManyFilesRemotes (776.97s)

The other benchmarks end up about the same within the margin of
variability, with one possible exception being that RemoteNeed seems to
be a little slower on average:

                                          old files/s   new files/s
    Update/n=RemoteNeed/size=1000-8            5.051k        4.654k
    Update/n=RemoteNeed/size=2000-8            5.201k        4.384k
    Update/n=RemoteNeed/size=4000-8            4.943k        4.242k
    Update/n=RemoteNeed/size=8000-8            5.099k        3.527k
    Update/n=RemoteNeed/size=16000-8           3.686k        3.847k
    Update/n=RemoteNeed/size=30000-8           4.456k        3.482k

I'm not sure why, possibly that query can be optimised anyhow.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-12 09:27:41 +00:00
Jakob Borg
dd90e8ec7a fix(api): limit size of allowed authentication request (#10386)
We have a slightly naive io.ReadAll on the authentication handler, which
can result in unlimited memory consumption from an unauthenticated API
endpoint. Add a reasonable limit there.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-11 10:11:29 +00:00
Jakob Borg
aa6ae0f3b0 fix(sqlite): add _txlock=immediate to modernc implementation (#10384)
For symmetry with the CGO variant.

https://pkg.go.dev/modernc.org/sqlite#Driver.Open

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-11 06:16:31 +00:00
Jakob Borg
e8b256793a chore: clean up migrated database (#10381)
Remove the migrated v0.14.0 database format after two weeks. Remove a
few old patterns that are no longer relevant. Ensure the cleanup runs in
both the config and database directories.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-10 12:23:35 +02:00
Catfriend1
8233279a65 chore(ursrv): update regex patterns for Syncthing-Fork entries (#10380)
Update regex patterns for Syncthing-Fork entries

Signed-off-by: Catfriend1 <16361913+Catfriend1@users.noreply.github.com>
2025-09-09 14:34:12 +02:00
Jakob Borg
8e5d5802cc chore(ursrv): calculate more fine-grained percentiles
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-09 07:37:39 +02:00
Jakob Borg
25ae01b0d7 chore(sqlite): skip database GC entirely when it's provably unnecessary (#10379)
Store the sequence number of the last GC sweep in a KV. Next time, if it
matches we can just skip GC because nothing has been added or removed.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-08 08:55:04 +02:00
Syncthing Release Automation
66583927f8 chore(gui, man, authors): update docs, translations, and contributors 2025-09-08 03:52:21 +00:00
Simon Frei
f0328abeaa chore(scanner): always return values to the pools when hashing blocks (#10377)
There are some return statements in between, but putting back the values
isn't my motivation (hardly ever happens), I just find this more
readable. Same with moving `hashLength`: Placed next to the pool the
connection with `sha256.New()` is closer.

Followup to:
chore(scanner): reduce memory pressure by using pools inside hasher #10222
6e26fab3a0

Signed-off-by: Simon Frei <freisim93@gmail.com>
2025-09-07 17:00:19 +02:00
Jakob Borg
4b8d07d91c fix(sqlite): explicitly set temporary directory location (fixes #10368) (#10376)
On Unixes, avoid the /tmp which is likely to become the chosen default.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-07 14:04:47 +02:00
Jakob Borg
c33daca3b4 fix(sqlite): less impactful periodic garbage collection (#10374)
Periodic garbage collection can take a long time on large folders. The worst
step is the one for blocks, which are typically orders of magnitude more
numerous than files or block lists.

This improves the situation in by running blocks GC in a number of smaller
range chunks, in random order, and stopping after a time limit. At most ten
minutes per run will be spent garbage collecting blocklists and blocks.

With this, we're not guaranteed to complete a full GC on every run, but
we'll make some progress and get there eventually.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2025-09-07 14:04:29 +02:00
Amin Vakil
a533f453f8 build: trigger nightly build only on syncthing repo (#10375)
Signed-off-by: Amin Vakil <info@aminvakil.com>
2025-09-07 14:03:33 +02:00
65 changed files with 1512 additions and 800 deletions

View File

@@ -30,11 +30,11 @@ jobs:
- stupgrades
- ursrv
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true

View File

@@ -51,12 +51,12 @@ jobs:
release-generation: ${{ steps.get-version.outputs.release-generation }}
go-version: ${{ steps.get-go.outputs.go-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache: false
@@ -116,9 +116,9 @@ jobs:
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
cache: true
@@ -168,7 +168,7 @@ jobs:
- golangci
- meta
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
#
# Windows
@@ -183,9 +183,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -292,9 +292,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -347,7 +347,7 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
GO_VERSION: ${{ needs.facts.outputs.go-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build syncthing in OmniOS VM
uses: vmactions/omnios-vm@v1
@@ -389,9 +389,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -517,9 +517,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -584,9 +584,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -633,9 +633,9 @@ jobs:
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: syncthing/release-tools
path: tools
@@ -643,7 +643,7 @@ jobs:
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -714,9 +714,9 @@ jobs:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -770,7 +770,7 @@ jobs:
- facts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: syncthing/release-tools
path: tools
@@ -781,7 +781,7 @@ jobs:
name: packages-signed
path: packages
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -825,7 +825,7 @@ jobs:
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
@@ -842,7 +842,7 @@ jobs:
name: debian-packages
path: packages
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -1004,9 +1004,9 @@ jobs:
dockerfile: Dockerfile.stdiscosrv
image: discosrv
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -1099,7 +1099,7 @@ jobs:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Sync images
uses: docker://docker.io/regclient/regsync:latest
with:
@@ -1117,9 +1117,9 @@ jobs:
needs:
- facts
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
@@ -1139,8 +1139,8 @@ jobs:
name: Run golangci-lint
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: 'stable'
@@ -1160,8 +1160,8 @@ jobs:
name: Run meta checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version: 'stable'

View File

@@ -15,13 +15,13 @@ jobs:
runs-on: ubuntu-latest
environment: release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: stable

View File

@@ -8,11 +8,12 @@ on:
jobs:
trigger-nightly:
if: github.repository_owner == 'syncthing'
runs-on: ubuntu-latest
name: Push to release-nightly to trigger build
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
fetch-depth: 0

View File

@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
name: Update translations and documentation
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with:
go-version: stable
- run: |

View File

@@ -8,17 +8,20 @@
policy:
approval:
- subject is conventional commit
- project metadata requires maintainer approval
- or:
- project metadata requires maintainer approval
- a maintainer claims responsibility
- or:
- is approved by a syncthing contributor
- is a translation or dependency update by a contributor
- is a trivial change by a contributor
- a maintainer claims responsibility
# Additionally, contributors can disapprove of a PR
# Additionally, maintainers can disapprove of a PR
disapproval:
requires:
teams:
- syncthing/contributors
- syncthing/maintainers
# The rules for the policy are described below.
@@ -96,3 +99,13 @@ approval_rules:
has_author_in:
teams:
- syncthing/contributors
# A member of the maintainers group can take responsibility by adding the
# appropriate label.
- name: a maintainer claims responsibility
if:
has_labels:
- maintainer-responsibility
has_author_in:
teams:
- syncthing/maintainers

View File

@@ -8,6 +8,7 @@ package serve
import (
"context"
"fmt"
"log/slog"
"reflect"
"slices"
@@ -335,12 +336,12 @@ func (q *metricSummary) Collect(c chan<- prometheus.Metric) {
}
slices.Sort(vs)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[0], append(labelVals, "0")...)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*5/100], append(labelVals, "0.05")...)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)/2], append(labelVals, "0.5")...)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*9/10], append(labelVals, "0.9")...)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*95/100], append(labelVals, "0.95")...)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)-1], append(labelVals, "1")...)
pctiles := []float64{0, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.975, 0.99, 1}
for _, pct := range pctiles {
idx := int(float64(len(vs)-1) * pct)
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[idx], append(labelVals, fmt.Sprint(pct))...)
}
}
}

View File

@@ -79,7 +79,8 @@ var (
{regexp.MustCompile(`\svagrant@bullseye`), "F-Droid"},
{regexp.MustCompile(`\svagrant@bookworm`), "F-Droid"},
{regexp.MustCompile(`Anwender@NET2017`), "Syncthing-Fork (3rd party)"},
{regexp.MustCompile(`\sreproducible-build@Catfriend1-syncthing-android`), "Syncthing-Fork Catfriend1 (3rd party)"},
{regexp.MustCompile(`\sreproducible-build@nel0x-syncthing-android-gplay`), "Syncthing-Fork nel0x (3rd party)"},
{regexp.MustCompile(`\sbuilduser@(archlinux|svetlemodry)`), "Arch (3rd party)"},
{regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"},

View File

@@ -781,40 +781,39 @@ func initialAutoUpgradeCheck(misc *db.Typed) (upgrade.Release, error) {
// suitable time after they have gone out of fashion.
func cleanConfigDirectory() {
patterns := map[string]time.Duration{
"panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
"audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
"index": 14 * 24 * time.Hour, // keep old index format for two weeks
"index-v0.11.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
"index-v0.13.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
"index*.converted": 14 * 24 * time.Hour, // keep old converted indexes for two weeks
"config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
"*.idx.gz": 30 * 24 * time.Hour, // these should for sure no longer exist
"backup-of-v0.8": 30 * 24 * time.Hour, // these neither
"tmp-index-sorter.*": time.Minute, // these should never exist on startup
"support-bundle-*": 30 * 24 * time.Hour, // keep old support bundle zip or folder for a month
"csrftokens.txt": 0, // deprecated, remove immediately
"panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
"audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
"index-v0.14.0.db-migrated": 14 * 24 * time.Hour, // keep old index format for two weeks
"config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
"support-bundle-*": 30 * 24 * time.Hour, // keep old support bundle zip or folder for a month
}
for pat, dur := range patterns {
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, locations.GetBaseDir(locations.ConfigBaseDir))
files, err := fs.Glob(pat)
if err != nil {
slog.Warn("Failed to clean config directory", slogutil.Error(err))
continue
}
for _, file := range files {
info, err := fs.Lstat(file)
locations := slices.Compact([]string{
locations.GetBaseDir(locations.ConfigBaseDir),
locations.GetBaseDir(locations.DataBaseDir),
})
for _, loc := range locations {
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, loc)
for pat, dur := range patterns {
entries, err := fs.Glob(pat)
if err != nil {
slog.Warn("Failed to clean config directory", slogutil.Error(err))
continue
}
if time.Since(info.ModTime()) > dur {
if err = fs.RemoveAll(file); err != nil {
for _, entry := range entries {
info, err := fs.Lstat(entry)
if err != nil {
slog.Warn("Failed to clean config directory", slogutil.Error(err))
} else {
slog.Warn("Cleaned away old file", slogutil.FilePath(filepath.Base(file)))
continue
}
if time.Since(info.ModTime()) > dur {
if err = fs.RemoveAll(entry); err != nil {
slog.Warn("Failed to clean config directory", slogutil.Error(err))
} else {
slog.Warn("Cleaned away old file", slogutil.FilePath(filepath.Base(entry)))
}
}
}
}

View File

@@ -18,6 +18,7 @@ import (
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"golang.org/x/exp/constraints"
)
@@ -61,7 +62,7 @@ func savePerfStats(file string) {
rss,
rate(prevIn, in, timeDiff, 1e3),
rate(prevOut, out, timeDiff, 1e3),
dirsize(locations.Get(locations.Database))/1024,
osutil.DirSize(locations.Get(locations.Database))/1024,
)
prevTime = t
@@ -84,21 +85,3 @@ func rate[T number](prev, cur T, d time.Duration, div float64) float64 {
rate := float64(diff) / d.Seconds() / div
return rate
}
func dirsize(location string) int64 {
entries, err := os.ReadDir(location)
if err != nil {
return 0
}
var size int64
for _, entry := range entries {
fi, err := entry.Info()
if err != nil {
continue
}
size += fi.Size()
}
return size
}

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Дазволеныя Сеткі",
"Alphabetic": "Па Алфавіту",
"Altered by ignoring deletes.": "Зменена з-за ігнаравання выдаленняў.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Заўсёды ўключана для папак з тыпам \"{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Знешняя каманда утрымлівае версіянаванне. Яно патрабуе выдалення файла з агульнай дырэкторыі. Калі шлях да прыкладання ўтрымлівае прабелы, трэба іх выдаліць.",
"Anonymous Usage Reporting": "Ананімная Спрадвыздача Аб Выкарыстанні",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Фармат ананімнай спрадвыздачы аб выкарыстанні быў зменены. Ці жадаеце вы выкарыстоўваць новы фармат?",

View File

@@ -177,7 +177,7 @@
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Вида „{{receiveEncrypted}}“ може да бъде избран само при добавяне на папка.",
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Видът папката „{{receiveEncrypted}}“ не може да бъде променян след нейното създаване. Трябва да я премахнете, изтриете или разшифровате съдържанието и да добавите папката отново.",
"Folders": "Папки",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете нейната първопричина или потърсете помощ ако не съумявате.",
"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.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете първопричината или ако не съумявате потърсете помощ.",
"Forever": "Завинаги",
"Full Rescan Interval (s)": "Интервал на пълно обхождане (секунди)",
"GUI": "Интерфейс",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabético",
"Altered by ignoring deletes.": "Alterado ignorando eliminaciones.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Siempre activado cuando el tipo de carpeta es \"{{foldertype}}\".",
"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.": "Un comando externo maneja las versiones. Tienes que eliminar el archivo de la carpeta compartida. Si la ruta a la aplicación contiene espacios, ésta debe estar entre comillas.",
"Anonymous Usage Reporting": "Informe anónimo de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe de uso anónimo a cambiado. ¿Le gustaría pasar al nuevo formato?",
@@ -52,6 +53,7 @@
"Body:": "Contenido:",
"Bugs": "Errores",
"Cancel": "Cancelar",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No se puede habilitar cuando el tipo de carpeta es \"{{foldertype}}\".",
"Changelog": "Registro de cambios",
"Clean out after": "Limpiar tras",
"Cleaning Versions": "Limpiando Versiones",
@@ -80,6 +82,7 @@
"Custom Range": "Rango personalizado",
"Danger!": "¡Peligro!",
"Database Location": "Ubicación de la base de datos",
"Debug": "Depurar",
"Debugging Facilities": "Servicios de depuración",
"Default": "Predeterminado",
"Default Configuration": "Configuración Predeterminada",
@@ -208,6 +211,7 @@
"Incoming Rate Limit (KiB/s)": "Límite de descarga (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configuración incorrecta puede corromper el contenido de la carpeta y poner a Syncthing en un estado inoperante.",
"Incorrect user name or password.": "Nombre de usuario o contraseña incorrectos.",
"Info": "Información",
"Internally used paths:": "Rutas de uso interno:",
"Introduced By": "Introducido por",
"Introducer": "Presentador",
@@ -225,6 +229,7 @@
"Learn more": "Saber más",
"Learn more at {%url%}": "Más información en {{url}}",
"Limit": "Límite",
"Limit Bandwidth in LAN": "Limitar el ancho de banda en LAN",
"Listener Failures": "Fallos de Oyente",
"Listener Status": "Estado de Oyente",
"Listeners": "Oyentes",

View File

@@ -1,6 +1,7 @@
{
"A device with that ID is already added.": "Sellise tunnusega seade on juba lisatud.",
"A negative number of days doesn't make sense.": "Negatiivne päevade arv ei ole loogiline.",
"A new major version may not be compatible with previous versions.": "Uus põhiversioon ei pruugi ühilduv varasemate versioonidega.",
"API Key": "API võti",
"About": "Rakenduse teave",
"Action": "Tegevus",
@@ -19,20 +20,34 @@
"Apply": "Rakenda",
"Automatic upgrades": "Automaatsed uuendused",
"Be careful!": "Ettevaatust!",
"Cancel": "Loobu",
"Changelog": "Muudatuste nimekiri",
"Bugs": "Vead",
"Cancel": "Katkesta",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kui kausta tüüp on „{{foldertype}}“, siis seda ei saa kasutada.",
"Changelog": "Muudatuste ajalugu",
"Close": "Sulge",
"Compression": "Pakkimine",
"Configuration Directory": "Seadistuste kaust",
"Configuration File": "Seadistuste fail",
"Configured": "Seadistatud",
"Connected (Unused)": "Ühendatud (pole kasutusel)",
"Connection Error": "Ühenduse viga",
"Connection Management": "Ühenduste haldus",
"Connection Type": "Ühenduse tüüp",
"Connections": "Ühendused",
"Copied from elsewhere": "Kopeeritud mujalt",
"Copied from original": "Kopeeritud algallikast",
"Copied!": "Kopeeritud!",
"Delete": "Kustuta",
"Device": "Seade",
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Seade \"{{name}}\" ({{device}} aadressil {{address}}) soovib luua ühendust. Kas lisada uus seade?",
"Device ID": "Seadme ID",
"Device Certificate": "Seadme sertifikaat",
"Device ID": "Seadme tunnus",
"Device Identification": "Seadme tuvastamine",
"Device Name": "Seadme nimi",
"Devices": "Seadmed",
"Disconnected": "Ühendus puudub",
"Do not restore": "Ära taasta",
"Do not restore all": "Ära taasta kõiki",
"Documentation": "Dokumentatsioon",
"Download Rate": "Allalaadimise Kiirus",
"Downloaded": "Alla laetud",
@@ -55,33 +70,48 @@
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
"GUI Theme": "GUI Teema",
"Generate": "Genereeri",
"Global State": "Globaalne Olek",
"Help": "Abi",
"Global State": "Üldine olek",
"Help": "Abiteave",
"Ignore": "Ignoreeri",
"Ignore Patterns": "Ignoreeri Mustreid",
"Ignore Permissions": "Ignoreeri Õigusi",
"Incoming Rate Limit (KiB/s)": "Siseneva Kiiruse Piirang (KiB/s)",
"Ignored Devices": "Eiratud seadmed",
"Ignored Folders": "Eiratud kaustad",
"Incoming Rate Limit (KiB/s)": "Siseneva liikluse kiiruspiirang (KiB/s)",
"Keep Versions": "Säilita Versioone",
"LDAP": "LDAP",
"Largest First": "Suurim Enne",
"Last 30 Days": "Viimased 30 päeva",
"Last 7 Days": "Viimased 7 päeva",
"Last Month": "Viimane kuu",
"Last Scan": "Viimane skaneerimine",
"Last seen": "Viimati nähtud",
"Latest Change": "Viimane Muudatus",
"Learn more": "Veel infot",
"Learn more at {%url%}": "Lisateavet leiad siit: {{url}}",
"Local State": "Kohalik Olek",
"Local State (Total)": "Kohalik Olek (Summaarne)",
"Maximum Age": "Maksimaalne Vanus",
"Metadata Only": "Ainult Meta-andmed",
"Maximum Age": "Maksimaalne vanus",
"Maximum single entry size": "Maksimaalne ühe objekti maht",
"Maximum total size": "Maksimaalne kogumaht",
"Metadata Only": "Ainult metaandmed",
"Minimum Free Disk Space": "Minimaalne Vaba Kettaruum",
"More than a month ago": "Enam, kui kuu tagasi",
"More than a week ago": "Enam, kui nädal tagasi",
"More than a year ago": "Enam, kui aasta tagasi",
"Move to top of queue": "Liiguta järjekorra algusesse",
"Never": "Eikunagi",
"New Device": "Uus Seade",
"New Folder": "Uus Kaust",
"Newest First": "Uusimad Ennem",
"No": "Ei",
"Number of Connections": "Ühenduste arv",
"OK": "Sobib",
"Oldest First": "Vanimad Ennem",
"Options": "Valikud",
"Outgoing Rate Limit (KiB/s)": "Väljuva Kiiruse Piirang (KiB/s)",
"Out of Sync": "Pole sünkroonis",
"Out of Sync Items": "Sünkroonimata objektid",
"Outgoing Rate Limit (KiB/s)": "Väljuva kiiruse piirang (KiB/s)",
"Override Changes": "Kirjuta Muudatused Üle",
"Pause": "Peata",
"Pause All": "Peata Kõik",

View File

@@ -1,11 +1,11 @@
{
"A device with that ID is already added.": "May naidagdag na device na may ganitong ding ID.",
"A negative number of days doesn't make sense.": "Walang saysay ang negatibong numero ng araw.",
"A new major version may not be compatible with previous versions.": "Maaring hindi compatible ang isang bagong major na beryson sa mga nakaraang bersyon.",
"A device with that ID is already added.": "May naidagdag na device na may ganito ring ID.",
"A negative number of days doesn't make sense.": "Walang saysay ang negatibong bilang ng araw.",
"A new major version may not be compatible with previous versions.": "Maaring hindi compatible ang isang bagong major na bersiyon sa mga nakaraang bersiyon.",
"API Key": "API Key",
"About": "Tungkol sa",
"Action": "Aksyon",
"Actions": "Mga Aksyon",
"Action": "Aksiyon",
"Actions": "Mga Aksiyon",
"Active filter rules": "Mga aktibong tuntunin sa pag-filter",
"Add": "Magdagdag",
"Add Device": "Magdagdag ng Device",
@@ -56,7 +56,7 @@
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Hindi maaaring paganahin kapag ang uri ng folder ay \"{{foldertype}}\".",
"Changelog": "Mga Pagbabago",
"Clean out after": "Linisin pagkatapos",
"Cleaning Versions": "Mga Bersyon ng Paglinis",
"Cleaning Versions": "Mga Bersiyon ng Paglinis",
"Cleanup Interval": "Pagitan ng Paglinis",
"Click to see full identification string and QR code.": "I-click upang makita ang buong string ng pagkakakilanlan at QR code.",
"Close": "Isara",
@@ -155,14 +155,14 @@
"External": "Panlabas",
"External File Versioning": "Panlabas na File Versioning",
"Failed Items": "Mga Nabigong Item",
"Failed to load file versions.": "Nabigong i-load ang mga bersyon ng file.",
"Failed to load file versions.": "Nabigong i-load ang mga bersiyon ng file.",
"Failed to load ignore patterns.": "Nabigong i-load ang mga ignore pattern.",
"Failed to set up, retrying": "Nabigong i-set up, sinusubukan muli",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Inaasahan ang pagbigo sa pagkonekta sa mga IPv6 na server kapag walang konektibidad sa IPv6.",
"File Pull Order": "Order ng Pagkuha ng File",
"File Versioning": "File Versioning",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Nilipat ang mga file sa .stversions na direktoryo kapag pinalitan o binura ng Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Nililipat ang mga file sa mga naka-date stamp na bersyon sa .stversions na direktoryo kapag pinalitan o binura ng Syncthing.",
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Nililipat ang mga file sa mga naka-date stamp na bersiyon sa .stversions na direktoryo kapag pinalitan o binura ng 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.": "Pinoprotektahan ang mga file mula sa mga pagbabago sa ibang device, pero ipapadala sa ibang cluster ang mga pagbabago na ginawa sa device na ito.",
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Sini-synchronize mula sa cluster ang mga file, pero hindi ipapadala sa mga ibang device ang mgaanumang pagbabago.",
"Filesystem Watcher Errors": "Mga Error sa Taganood ng Filesystem",
@@ -217,7 +217,7 @@
"Introducer": "Tagapagpakilala",
"Introduction": "Panimula",
"Inversion of the given condition (i.e. do not exclude)": "Pabaliktad ng ibinigay na kundisyon (hal. huwag ibukod)",
"Keep Versions": "Panatilihin ang mga Bersyon",
"Keep Versions": "Panatilihin ang mga Bersiyon",
"LDAP": "LDAP",
"Largest First": "Pinakamalaki Muna",
"Last 30 Days": "Huling 30 Araw",
@@ -245,12 +245,12 @@
"Log In": "Mag-Log In",
"Log Out": "Mag-Log Out",
"Log in to see paths information.": "Mag-log in upang makita ang impormasyon ng mga path.",
"Log in to see version information.": "Mag-log in upang makita ang impormasyon ng bersyon.",
"Log in to see version information.": "Mag-log in upang makita ang impormasyon ng bersiyon.",
"Log tailing paused. Scroll to the bottom to continue.": "Na-pause ang tailing ng tala. Mag-scroll pababa para magpatuloy.",
"Login failed, see Syncthing logs for details.": "Nabigo ang pag-login, tignan ang mga tala ng Syncthing para sa mga detalye.",
"Logs": "Mga Tala",
"Major Upgrade": "Major na Upgrade",
"Mass actions": "Mga maramihang aksyon",
"Mass actions": "Mga maramihang aksiyon",
"Maximum Age": "Pinakamataas na Edad",
"Maximum single entry size": "Pinakamataas na laki ng isang entry",
"Maximum total size": "Pinakamataas na kabuuang laki",
@@ -289,7 +289,7 @@
"Password": "Password",
"Path": "Path",
"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": "Path papunta sa folder sa computer na ito. Gagawin kapag hindi umiiral. Maaring gamitin ang tilde na character (~) bilang shortcut sa",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path kung saan ilalagay ang mga bersyon (iwanang walang laman para sa default .stversions na direktoryo sa binabahaging folder).",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path kung saan ilalagay ang mga bersiyon (iwanang walang laman para sa default .stversions na direktoryo sa binabahaging folder).",
"Paths": "Mga Path",
"Pause": "I-pause",
"Pause All": "I-pause Lahat",
@@ -335,7 +335,7 @@
"Restart Needed": "Kinakailangan ng Restart",
"Restarting": "Nagre-restart",
"Restore": "I-restore",
"Restore Versions": "I-restore ang Mga Bersyon",
"Restore Versions": "I-restore ang Mga Bersiyon",
"Resume": "I-resume",
"Resume All": "I-resume Lahat",
"Reused": "Ginamit muli",
@@ -345,13 +345,13 @@
"Saving changes": "Sine-save ang mga pagbabago",
"Scan Time Remaining": "Natitirang Oras sa Pag-scan",
"Scanning": "Sina-scan",
"See external versioning help for supported templated command line parameters.": "Tingnan ang tulong sa external na pag-bersyon para sa mga sinusuportahang naka-template na parameter ng command line.",
"See external versioning help for supported templated command line parameters.": "Tingnan ang tulong sa external na pag-bersiyon para sa mga sinusuportahang naka-template na parameter ng command line.",
"Select All": "Piliin Lahat",
"Select a version": "Pumili ng bersyon",
"Select a version": "Pumili ng bersiyon",
"Select additional devices to share this folder with.": "Pumili ng mga karagdagang device para ibagagi ang folder na ito.",
"Select additional folders to share with this device.": "Pumili ng mga karagdagang folder para ibagagi sa device na ito.",
"Select latest version": "Piliin ang pinakabagong bersyon",
"Select oldest version": "Piliin ang pinakalumang bersyon",
"Select latest version": "Piliin ang pinakabagong bersiyon",
"Select oldest version": "Piliin ang pinakalumang bersiyon",
"Send & Receive": "Magpadala at Makatanggap",
"Send Extended Attributes": "Magpadala ng mga Pinalawak na Attribute",
"Send Only": "Magpadala Lamang",
@@ -370,7 +370,7 @@
"Show QR": "Ipakita ang QR",
"Show detailed discovery status": "Magpakita ng detalyadong status sa pagtuklas",
"Show detailed listener status": "Ipakita ang detalyadong status sa listener",
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersyon",
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersiyon",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Ipinapakita sa halip na Device ID sa status ng cluster. Ia-advertise sa iba pang mga device bilang opsyonal na default na pangalan.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Ipinapakita sa halip na Device ID sa status ng cluster. Ia-update sa pangalan na ina-advertise ng device kung iiwanang walang laman.",
"Shut Down": "I-shutdown",
@@ -425,15 +425,15 @@
"The cleanup interval cannot be blank.": "Hindi maaaring walang laman ang pagitan ng paglinis.",
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Na-save na ang configuration ngunit hindi naka-activate. Kailangang mag-restart ang Syncthing para i-activate ang bagong configuration.",
"The device ID cannot be blank.": "Hindi maaaring walang laman ang Device ID.",
"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).": "Mahahanap ang device ID na ilalagay dito sa \"Mga Aksyon > Ipakita ang ID\" na dialog sa isa pang device. Opsyonal ang mga puwang at gitling (hindi pinapansin).",
"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.": "Araw-araw na pinapadala ang naka-encrypt na ulat sa paggamit. Ginagamit ito sa pag-track ng mga karaniwang platform, laki ng folder, at bersyon ng app. Kapag nabago ang tinakdang data ng ulat ipo-prompt kang muli ng dialog na ito.",
"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).": "Mahahanap ang device ID na ilalagay dito sa \"Mga Aksiyon > Ipakita ang ID\" na dialog sa isa pang device. Opsyonal ang mga puwang at gitling (hindi pinapansin).",
"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.": "Araw-araw na pinapadala ang naka-encrypt na ulat sa paggamit. Ginagamit ito sa pag-track ng mga karaniwang platform, laki ng folder, at bersiyon ng app. Kapag nabago ang tinakdang data ng ulat ipo-prompt kang muli ng dialog na ito.",
"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.": "Mukhang hindi angkop ang inilagay na device ID. Dapat itong 52 o 56 na character na string na binubuo ng mga titik at numero, na may mga puwang at gitling bilang opsyonal.",
"The folder ID cannot be blank.": "Hindi maaaring walang laman ang folder ID.",
"The folder ID must be unique.": "Kailangang kakaiba ang folder ID.",
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Io-overwrite ang nilalaman ng folder sa mga ibang device para maging magkapareho sa device na ito. Ang mga file na hindi nandito ay buburahin sa mga ibang device.",
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Io-overwrite ang nilalaman ng folder sa mga ibang device para maging magkapareho sa device na ito. Ang mga file na kamakilang dinagdag dito ay buburahin sa mga ibang device.",
"The folder path cannot be blank.": "Hindi maaaring walang laman ang path ng folder.",
"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.": "Ang mga sumusunod na pagitan ay ginagamit: sa unang oras ang isang bersyon ay pinapanatili bawat 30 segundo, sa unang araw ang isang bersyon ay pinapanatili bawat oras, sa unang 30 araw ang isang bersyon ay pinapanatili bawat araw, hanggang sa pinakamataas na edad ang isang bersyon ay pinapanatili bawat linggo.",
"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.": "Ang mga sumusunod na pagitan ay ginagamit: sa unang oras ang isang bersiyon ay pinapanatili bawat 30 segundo, sa unang araw ang isang bersiyon ay pinapanatili bawat oras, sa unang 30 araw ang isang bersiyon ay pinapanatili bawat araw, hanggang sa pinakamataas na edad ang isang bersiyon ay pinapanatili bawat linggo.",
"The following items could not be synchronized.": "Hindi ma-synchronize ang mga sumusunod na item.",
"The following items were changed locally.": "Binago ng lokal ang mga sumusunod na item.",
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Ang mga sumusunod na paraan ay ginagamit upang tumuklas ng mga ibang device sa network at ipahayag ang device na ito na mahanap ng iba:",
@@ -442,12 +442,12 @@
"The interval must be a positive number of seconds.": "Dapat positibong numero ng segundo ang pagitan.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Ang pagitan, bilang segundo, para sa pagtakbo ng paglinis sa versions na direktoryo. Sero para i-disable ang periodical na paglinis.",
"The maximum age must be a number and cannot be blank.": "Dapat numero ang pinakamataas na edad at hindi maaaring walang laman.",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Ang pinakamataas na oras para panatilihin ang bersyon (bilang araw, itakda sa 0 para panatilihin ang mga bersyon magpakailanman).",
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Ang pinakamataas na oras para panatilihin ang bersiyon (bilang araw, itakda sa 0 para panatilihin ang mga bersiyon magpakailanman).",
"The number of connections must be a non-negative number.": "Dapat hindi negatibong numero ang bilang ng mga koneksyon.",
"The number of days must be a number and cannot be blank.": "Dapat numero ang bilang ng araw at hindi maaaring walang laman.",
"The number of days to keep files in the trash can. Zero means forever.": "Ang bilang ng araw para panatilihin ang mga file sa basurahan. Ang sero ay ibig sabihin ay magpakailanman.",
"The number of old versions to keep, per file.": "Ang bilang ng mga lumang bersyon na dapat panatilihin, bawat file.",
"The number of versions must be a number and cannot be blank.": "Dapat numero ang bilang ng mga bersyon at hindi maaaring walang laman.",
"The number of old versions to keep, per file.": "Ang bilang ng mga lumang bersiyon na dapat panatilihin, bawat file.",
"The number of versions must be a number and cannot be blank.": "Dapat numero ang bilang ng mga bersiyon at hindi maaaring walang laman.",
"The path cannot be blank.": "Hindi maaaring walang laman ang path.",
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ina-apply ang rate limit sa naipon na traffic ng lahat ng mga koneksyon sa device na ito.",
"The rate limit must be a non-negative number (0: no limit)": "Dapat hindi negatibong numero ang rate limit (0: walang limitasyon)",
@@ -462,7 +462,7 @@
"This Month": "Itong Buwan",
"This can easily give hackers access to read and change any files on your computer.": "Madali nitong mabibigyan ang mga hacker ng access na basahin at baguhin ang anumang mga file sa iyong computer.",
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Hindi awtomatikong tutuklasin ng device na ito ng mga ibang device o i-annouce ang sarili nitong address para mahanap ng iba. Makakakonekta lamang ang mga device na may static na naka-configure na address.",
"This is a major version upgrade.": "Ito ay isang upgrade sa major na bersyon.",
"This is a major version upgrade.": "Ito ay isang upgrade sa major na bersiyon.",
"This setting controls the free space required on the home (i.e., index database) disk.": "Kinokontrol ng setting na ito ang kinakailangan na bakanteng espasyo sa home (hal., index database) disk.",
"Time": "Oras",
"Time the item was last modified": "Oras na huling binago ang file",
@@ -501,10 +501,10 @@
"Using a QUIC connection over WAN": "Gumagamit ng QUIC na koneksyon mula sa WAN",
"Using a direct TCP connection over LAN": "Gumagamit ng direktang TCP na koneksyon mula sa LAN",
"Using a direct TCP connection over WAN": "Gumagamit ng direktang TCP na koneksyon mula sa WAN",
"Version": "Bersyon",
"Versions": "Mga Bersyon",
"Versions Path": "Path ng Mga Bersyon",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Awtomatikong buburahin ang mgs bersyon kapag mas matanda sila kaysa sa pinakamataas na edad o lumalagpas sa numero ng mga file na pinapayagan sa pagitan.",
"Version": "Bersiyon",
"Versions": "Mga Bersiyon",
"Versions Path": "Path ng Mga Bersiyon",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Awtomatikong buburahin ang mgs bersiyon kapag mas matanda sila kaysa sa pinakamataas na edad o lumalagpas sa numero ng mga file na pinapayagan sa pagitan.",
"Waiting to Clean": "Naghihintay para Linisin",
"Waiting to Scan": "Naghihintay para Mag-scan",
"Waiting to Sync": "Naghihintay para Mag-sync",
@@ -529,7 +529,7 @@
"You have no ignored devices.": "Wala kang mga hindi pinapansin na device.",
"You have no ignored folders.": "Wala kang mga hindi pinapansin na folder.",
"You have unsaved changes. Do you really want to discard them?": "Mayroon kang mga hindi na-save na pagbabago. Gusto mo ba talagang i-discard ang mga ito?",
"You must keep at least one version.": "Kailangan mong magpanatili ng kahit isang bersyon.",
"You must keep at least one version.": "Kailangan mong magpanatili ng kahit isang bersiyon.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Hindi ka dapat magdagdag o magpalit ng anumang lokal sa folder na \"{{receiveEncrypted}}\".",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Magbubukas ang iyong SMS app para hayaan kang pumili ng tatanggap at ipadala ito mula sa sarili mong numero.",
"Your email app should open to let you choose the recipient and send it from your own address.": "Magbubukas ang iyong email app para hayaan kang pumili ng tatanggap at ipadala mula sa sarili mong address.",

View File

@@ -256,8 +256,8 @@
"Maximum total size": "Taille maximum totale",
"Metadata Only": "Métadonnées uniquement",
"Minimum Free Disk Space": "Espace disque libre minimum",
"Mod. Device": "Appareil modificateur",
"Mod. Time": "Date de modification",
"Mod. Device": "Modificateur",
"Mod. Time": "Dernière modif.",
"More than a month ago": "Plus d'un mois",
"More than a week ago": "Plus d'une semaine",
"More than a year ago": "Plus d'un an",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabética",
"Altered by ignoring deletes.": "Cambiado por ignorar o borrado.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre acendido cando o cartafol é de tipo \"{{foldertype}}\".",
"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.": "Un comando externo xestiona as versións. Ten que eliminar o ficheiro do cartafol compartido. Si a ruta ao aplicativo contén espazos, deberían ir acotados.",
"Anonymous Usage Reporting": "Informe anónimo de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do informe de uso anónimo cambiou. Quere usar o novo formato?",
@@ -52,6 +53,7 @@
"Body:": "Corpo:",
"Bugs": "Erros",
"Cancel": "Cancelar",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Non se pode activar cando o cartafol é de tipo \"{{foldertype}}\".",
"Changelog": "Rexistro de cambios",
"Clean out after": "Limpar despois",
"Cleaning Versions": "Limpando Versións",
@@ -80,6 +82,7 @@
"Custom Range": "Rango personalizado",
"Danger!": "Perigo!",
"Database Location": "Localización da Base de Datos",
"Debug": "Depurar",
"Debugging Facilities": "Ferramentas de depuración",
"Default": "Predeterminado",
"Default Configuration": "Configuración Predeterminada",
@@ -140,6 +143,7 @@
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Activa o envío de atributos extendidos a outros dispositivos, e aplicar os atributos extendidos recibidos. Podería requerir a execución con privilexios elevados.",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Activa o envío de atributos extendidos a outros dispositivos, pero non aplica atributos extendidos que se reciben. Isto podería afectar significativamente ao rendemento. Sempre está activado cando «Sincr Atributos Extendidos\" está activado.",
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Activa o envío de información sobre a propiedade a outros dispositivos, e aplica a información sobre a propiedade cando se recibe. Normalmente require a execución con privilexios elevados.",
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "Activa o envío a outros dispositivos de información sobre a propiedade, pero non aplica información entrante sobre a propiedade. Isto pode afectar en gran medida ao rendemento. Está sempre activado cando \"Sincronización da propiedade\" está activada.",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduza un número non negativo (por exemplo, \"2.35\") e seleccione unha unidade. As porcentaxes son como partes totais do tamaño do disco.",
"Enter a non-privileged port number (1024 - 65535).": "Introduza un número de porto non privilexiado (1024-65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduza direccións separadas por comas (\"tcp://ip:porto\", \"tcp://host:porto\") ou \"dynamic\" para realizar o descubrimento automático da dirección.",

View File

@@ -82,6 +82,7 @@
"Custom Range": "טווח מותאם אישית",
"Danger!": "סכנה!",
"Database Location": "מיקום מסד נתונים",
"Debug": "איתור באגים",
"Debugging Facilities": "מתקני דיבוג",
"Default": "ברירת מחדל",
"Default Configuration": "תצורת ברירת מחדל",
@@ -210,6 +211,7 @@
"Incoming Rate Limit (KiB/s)": "מגבלת קצב נכנס (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "תצורה שגויה עלולה לגרום נזק לתכולת התיקייה שלך ולהפוך את Syncthing לבלתי ניתן להפעלה.",
"Incorrect user name or password.": "שם משתמש או סיסמה שגויים.",
"Info": "מידע",
"Internally used paths:": "נתיבים בשימוש פנימי:",
"Introduced By": "הוצג על ידי",
"Introducer": "מציג",

View File

@@ -187,13 +187,14 @@ angular.module('syncthing.core')
$scope.version = data;
}).error($scope.emitHTTPError);
$http.get(urlbase + '/svc/report').success(function (data) {
$scope.reportData = data;
if ($scope.system && $scope.config.options.urAccepted > -1 && $scope.config.options.urSeen < $scope.system.urVersionMax && $scope.config.options.urAccepted < $scope.system.urVersionMax) {
// Usage reporting format has changed, prompt the user to re-accept.
if ($scope.system && $scope.config.options.urAccepted > -1 && $scope.config.options.urSeen < $scope.system.urVersionMax && $scope.config.options.urAccepted < $scope.system.urVersionMax) {
// Usage reporting decision has not been taken or format
// has changed, prompt the user to (re-)accept.
$http.get(urlbase + '/svc/report').success(function (data) {
$scope.reportData = data;
showModal('#ur');
}
}).error($scope.emitHTTPError);
}).error($scope.emitHTTPError);
}
$http.get(urlbase + '/system/upgrade').success(function (data) {
$scope.upgradeInfo = data;

View File

@@ -7,6 +7,7 @@
package sqlite
import (
"context"
"database/sql"
"embed"
"io/fs"
@@ -26,7 +27,7 @@ import (
)
const (
currentSchemaVersion = 4
currentSchemaVersion = 5
applicationIDMain = 0x53546d6e // "STmn", Syncthing main database
applicationIDFolder = 0x53546664 // "STfd", Syncthing folder database
)
@@ -87,7 +88,31 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
},
}
tx, err := db.sql.Beginx()
// Create a specific connection for the schema setup and migration to
// run in. We do this because we need to disable foreign keys for the
// duration, which is a thing that needs to happen outside of a
// transaction and affects the connection it's run on. So we need to a)
// make sure all our commands run on this specific connection (which the
// transaction accomplishes naturally) and b) make sure these pragmas
// don't leak to anyone else afterwards.
ctx := context.TODO()
conn, err := db.sql.Connx(ctx)
if err != nil {
return nil, wrap(err)
}
defer func() {
_, _ = conn.ExecContext(ctx, "PRAGMA foreign_keys = ON")
_, _ = conn.ExecContext(ctx, "PRAGMA legacy_alter_table = OFF")
conn.Close()
}()
if _, err := conn.ExecContext(ctx, "PRAGMA foreign_keys = OFF"); err != nil {
return nil, wrap(err)
}
if _, err := conn.ExecContext(ctx, "PRAGMA legacy_alter_table = ON"); err != nil {
return nil, wrap(err)
}
tx, err := conn.BeginTxx(ctx, nil)
if err != nil {
return nil, wrap(err)
}
@@ -100,7 +125,7 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
}
ver, _ := db.getAppliedSchemaVersion(tx)
shouldVacuum := false
appliedMigrations := false
if ver.SchemaVersion > 0 {
filter := func(scr string) bool {
scr = filepath.Base(scr)
@@ -114,7 +139,7 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
}
if int(n) > ver.SchemaVersion {
slog.Info("Applying database migration", slogutil.FilePath(db.baseName), slog.String("script", scr))
shouldVacuum = true
appliedMigrations = true
return true
}
return false
@@ -124,6 +149,24 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
return nil, wrap(err)
}
}
// Run the initial schema scripts once more. This is generally a
// no-op. However, dropping a table removes associated triggers etc,
// and that's a thing we sometimes do in migrations. To avoid having
// to repeat the setup of associated triggers and indexes in the
// migration, we re-run the initial schema scripts.
for _, script := range schemaScripts {
if err := db.runScripts(tx, script); err != nil {
return nil, wrap(err)
}
}
// Finally, ensure nothing we've done along the way has violated key integrity.
if appliedMigrations {
if _, err := conn.ExecContext(ctx, "PRAGMA foreign_key_check"); err != nil {
return nil, wrap(err)
}
}
}
// Set the current schema version, if not already set
@@ -135,7 +178,7 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
return nil, wrap(err)
}
if shouldVacuum {
if appliedMigrations {
// We applied migrations and should take the opportunity to vaccuum
// the database.
if err := db.vacuumAndOptimize(); err != nil {
@@ -271,7 +314,12 @@ nextScript:
// also statement-internal semicolons in the triggers.
for _, stmt := range strings.Split(string(bs), "\n;") {
if _, err := tx.Exec(s.expandTemplateVars(stmt)); err != nil {
return wrap(err, stmt)
if strings.Contains(stmt, "syncthing:ignore-failure") {
// We're ok with this failing. Just note it.
slog.Debug("Script failed, but with ignore-failure annotation", slog.String("script", scr), slogutil.Error(wrap(err, stmt)))
} else {
return wrap(err, stmt)
}
}
}
}

View File

@@ -8,11 +8,13 @@ package sqlite
import (
"fmt"
"os"
"testing"
"time"
"github.com/syncthing/syncthing/internal/timeutil"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
)
@@ -223,7 +225,7 @@ func BenchmarkUpdate(b *testing.B) {
}
func TestBenchmarkDropAllRemote(t *testing.T) {
if testing.Short() {
if testing.Short() || os.Getenv("LONG_TEST") == "" {
t.Skip("slow test")
}
@@ -266,3 +268,61 @@ func TestBenchmarkDropAllRemote(t *testing.T) {
d := time.Since(t0)
t.Log("drop all took", d)
}
func TestBenchmarkSizeManyFilesRemotes(t *testing.T) {
// Reports the database size for a setup with many files and many remote
// devices each announcing every files, with fairly long file names and
// "worst case" version vectors.
if testing.Short() || os.Getenv("LONG_TEST") == "" {
t.Skip("slow test")
}
dir := t.TempDir()
db, err := Open(dir)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Fatal(err)
}
})
// This is equivalent to about 800 GiB in 100k files (i.e., 8 MiB per
// file), shared between 31 devices where each have touched every file.
const numFiles = 1e5
const numRemotes = 30
const numBlocks = 64
const filenameLen = 64
fs := make([]protocol.FileInfo, 1000)
n := 0
seq := 0
for n < numFiles {
for i := range fs {
seq++
fs[i] = genFile(rand.String(filenameLen), numBlocks, seq)
for r := range numRemotes {
fs[i].Version = fs[i].Version.Update(42 + protocol.ShortID(r))
}
}
if err := db.Update(folderID, protocol.LocalDeviceID, fs); err != nil {
t.Fatal(err)
}
for r := range numRemotes {
if err := db.Update(folderID, protocol.DeviceID{byte(42 + r)}, fs); err != nil {
t.Fatal(err)
}
}
n += len(fs)
t.Log(n, (numRemotes+1)*n)
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
size := osutil.DirSize(dir)
t.Logf("Total size: %.02f MiB", float64(size)/1024/1024)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/build"
)
const (
@@ -65,6 +66,8 @@ func Open(path string, opts ...Option) (*DB, error) {
}
_ = os.MkdirAll(path, 0o700)
initTmpDir(path)
mainPath := filepath.Join(path, "main.db")
mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations)
if err != nil {
@@ -114,6 +117,8 @@ func OpenForMigration(path string) (*DB, error) {
}
_ = os.MkdirAll(path, 0o700)
initTmpDir(path)
mainPath := filepath.Join(path, "main.db")
mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations)
if err != nil {
@@ -143,3 +148,24 @@ func (s *DB) Close() error {
}
return wrap(s.baseDB.Close())
}
func initTmpDir(path string) {
if build.IsWindows || build.IsDarwin || os.Getenv("SQLITE_TMPDIR") != "" {
// Doesn't use SQLITE_TMPDIR, isn't likely to have a tiny
// ram-backed temp directory, or already set to something.
return
}
// Attempt to override the SQLite temporary directory by setting the
// env var prior to the (first) database being opened and hence
// SQLite becoming initialized. We set the temp dir to the same
// place we store the database, in the hope that there will be
// enough space there for the operations it needs to perform, as
// opposed to /tmp and similar, on some systems.
dbTmpDir := filepath.Join(path, ".tmp")
if err := os.MkdirAll(dbTmpDir, 0o700); err == nil {
os.Setenv("SQLITE_TMPDIR", dbTmpDir)
} else {
slog.Warn("Failed to create temp directory for SQLite", slogutil.FilePath(dbTmpDir), slogutil.Error(err))
}
}

View File

@@ -15,7 +15,7 @@ import (
const (
dbDriver = "sqlite"
commonOptions = "_pragma=foreign_keys(1)&_pragma=recursive_triggers(1)&_pragma=synchronous(1)"
commonOptions = "_pragma=foreign_keys(1)&_pragma=recursive_triggers(1)&_pragma=synchronous(1)&_txlock=immediate"
)
func init() {

View File

@@ -8,19 +8,28 @@ package sqlite
import (
"context"
"encoding/binary"
"fmt"
"log/slog"
"math/rand"
"strings"
"time"
"github.com/jmoiron/sqlx"
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/thejerf/suture/v4"
)
const (
internalMetaPrefix = "dbsvc"
lastMaintKey = "lastMaint"
internalMetaPrefix = "dbsvc"
lastMaintKey = "lastMaint"
lastSuccessfulGCSeqKey = "lastSuccessfulGCSeq"
gcMinChunks = 5
gcChunkSize = 100_000 // approximate number of rows to process in a single gc query
gcMaxRuntime = 5 * time.Minute // max time to spend on gc, per table, per run
)
func (s *DB) Service(maintenanceInterval time.Duration) suture.Service {
@@ -91,16 +100,44 @@ func (s *Service) periodic(ctx context.Context) error {
}
return wrap(s.sdb.forEachFolder(func(fdb *folderDB) error {
fdb.updateLock.Lock()
defer fdb.updateLock.Unlock()
// Get the current device sequence, for comparison in the next step.
seq, err := fdb.GetDeviceSequence(protocol.LocalDeviceID)
if err != nil {
return wrap(err)
}
// Get the last successful GC sequence. If it's the same as the
// current sequence, nothing has changed and we can skip the GC
// entirely.
meta := db.NewTyped(fdb, internalMetaPrefix)
if prev, _, err := meta.Int64(lastSuccessfulGCSeqKey); err != nil {
return wrap(err)
} else if seq == prev {
slog.DebugContext(ctx, "Skipping unnecessary GC", "folder", fdb.folderID, "fdb", fdb.baseName)
return nil
}
if err := garbageCollectOldDeletedLocked(ctx, fdb); err != nil {
// Run the GC steps, in a function to be able to use a deferred
// unlock.
if err := func() error {
fdb.updateLock.Lock()
defer fdb.updateLock.Unlock()
if err := garbageCollectOldDeletedLocked(ctx, fdb); err != nil {
return wrap(err)
}
if err := garbageCollectNamesAndVersions(ctx, fdb); err != nil {
return wrap(err)
}
if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil {
return wrap(err)
}
return tidy(ctx, fdb.sql)
}(); err != nil {
return wrap(err)
}
if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil {
return wrap(err)
}
return tidy(ctx, fdb.sql)
// Update the successful GC sequence.
return wrap(meta.PutInt64(lastSuccessfulGCSeqKey, seq))
}))
}
@@ -118,8 +155,36 @@ func tidy(ctx context.Context, db *sqlx.DB) error {
return nil
}
func garbageCollectNamesAndVersions(ctx context.Context, fdb *folderDB) error {
l := slog.With("folder", fdb.folderID, "fdb", fdb.baseName)
res, err := fdb.stmt(`
DELETE FROM file_names
WHERE NOT EXISTS (SELECT 1 FROM files f WHERE f.name_idx = idx)
`).Exec()
if err != nil {
return wrap(err, "delete names")
}
if aff, err := res.RowsAffected(); err == nil {
l.DebugContext(ctx, "Removed old file names", "affected", aff)
}
res, err = fdb.stmt(`
DELETE FROM file_versions
WHERE NOT EXISTS (SELECT 1 FROM files f WHERE f.version_idx = idx)
`).Exec()
if err != nil {
return wrap(err, "delete versions")
}
if aff, err := res.RowsAffected(); err == nil {
l.DebugContext(ctx, "Removed old file versions", "affected", aff)
}
return nil
}
func garbageCollectOldDeletedLocked(ctx context.Context, fdb *folderDB) error {
l := slog.With("fdb", fdb.baseDB)
l := slog.With("folder", fdb.folderID, "fdb", fdb.baseName)
if fdb.deleteRetention <= 0 {
slog.DebugContext(ctx, "Delete retention is infinite, skipping cleanup")
return nil
@@ -171,37 +236,108 @@ func garbageCollectBlocklistsAndBlocksLocked(ctx context.Context, fdb *folderDB)
}
defer tx.Rollback() //nolint:errcheck
if res, err := tx.ExecContext(ctx, `
DELETE FROM blocklists
WHERE NOT EXISTS (
SELECT 1 FROM files WHERE files.blocklist_hash = blocklists.blocklist_hash
)`); err != nil {
return wrap(err, "delete blocklists")
} else {
slog.DebugContext(ctx, "Blocklist GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
rows, err := res.RowsAffected()
if err != nil {
return slogutil.Error(err)
}
return slog.Int64("rows", rows)
}))
}
// Both blocklists and blocks refer to blocklists_hash from the files table.
for _, table := range []string{"blocklists", "blocks"} {
// Count the number of rows
var rows int64
if err := tx.GetContext(ctx, &rows, `SELECT count(*) FROM `+table); err != nil {
return wrap(err)
}
if res, err := tx.ExecContext(ctx, `
DELETE FROM blocks
WHERE NOT EXISTS (
SELECT 1 FROM blocklists WHERE blocklists.blocklist_hash = blocks.blocklist_hash
)`); err != nil {
return wrap(err, "delete blocks")
} else {
slog.DebugContext(ctx, "Blocks GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
rows, err := res.RowsAffected()
if err != nil {
return slogutil.Error(err)
chunks := max(gcMinChunks, rows/gcChunkSize)
l := slog.With("folder", fdb.folderID, "fdb", fdb.baseName, "table", table, "rows", rows, "chunks", chunks)
// Process rows in chunks up to a given time limit. We always use at
// least gcMinChunks chunks, then increase the number as the number of rows
// exceeds gcMinChunks*gcChunkSize.
t0 := time.Now()
for i, br := range randomBlobRanges(int(chunks)) {
if d := time.Since(t0); d > gcMaxRuntime {
l.InfoContext(ctx, "GC was interrupted due to exceeding time limit", "processed", i, "runtime", time.Since(t0))
break
}
return slog.Int64("rows", rows)
}))
// The limit column must be an indexed column with a mostly random distribution of blobs.
// That's the blocklist_hash column for blocklists, and the hash column for blocks.
limitColumn := table + ".blocklist_hash"
if table == "blocks" {
limitColumn = "blocks.hash"
}
q := fmt.Sprintf(`
DELETE FROM %s
WHERE %s AND NOT EXISTS (
SELECT 1 FROM files WHERE files.blocklist_hash = %s.blocklist_hash
)`, table, br.SQL(limitColumn), table)
if res, err := tx.ExecContext(ctx, q); err != nil {
return wrap(err, "delete from "+table)
} else {
l.DebugContext(ctx, "GC query result", "processed", i, "runtime", time.Since(t0), "result", slogutil.Expensive(func() any {
rows, err := res.RowsAffected()
if err != nil {
return slogutil.Error(err)
}
return slog.Int64("rows", rows)
}))
}
}
}
return wrap(tx.Commit())
}
// blobRange defines a range for blob searching. A range is open ended if
// start or end is nil.
type blobRange struct {
start, end []byte
}
// SQL returns the SQL where clause for the given range, e.g.
// `column >= x'49249248' AND column < x'6db6db6c'`
func (r blobRange) SQL(name string) string {
var sb strings.Builder
if r.start != nil {
fmt.Fprintf(&sb, "%s >= x'%x'", name, r.start)
}
if r.start != nil && r.end != nil {
sb.WriteString(" AND ")
}
if r.end != nil {
fmt.Fprintf(&sb, "%s < x'%x'", name, r.end)
}
return sb.String()
}
// randomBlobRanges returns n blobRanges in random order
func randomBlobRanges(n int) []blobRange {
ranges := blobRanges(n)
rand.Shuffle(len(ranges), func(i, j int) { ranges[i], ranges[j] = ranges[j], ranges[i] })
return ranges
}
// blobRanges returns n blobRanges
func blobRanges(n int) []blobRange {
// We use three byte (24 bit) prefixes to get fairly granular ranges and easy bit
// conversions.
rangeSize := (1 << 24) / n
ranges := make([]blobRange, 0, n)
var prev []byte
for i := range n {
var pref []byte
if i < n-1 {
end := (i + 1) * rangeSize
pref = intToBlob(end)
}
ranges = append(ranges, blobRange{prev, pref})
prev = pref
}
return ranges
}
func intToBlob(n int) []byte {
var pref [4]byte
binary.BigEndian.PutUint32(pref[:], uint32(n)) //nolint:gosec
// first byte is always zero and not part of the range
return pref[1:]
}

View File

@@ -0,0 +1,37 @@
// Copyright (C) 2025 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package sqlite
import (
"bytes"
"fmt"
"strings"
"testing"
)
func TestBlobRange(t *testing.T) {
exp := `
hash < x'249249'
hash >= x'249249' AND hash < x'492492'
hash >= x'492492' AND hash < x'6db6db'
hash >= x'6db6db' AND hash < x'924924'
hash >= x'924924' AND hash < x'b6db6d'
hash >= x'b6db6d' AND hash < x'db6db6'
hash >= x'db6db6'
`
ranges := blobRanges(7)
buf := new(bytes.Buffer)
for _, r := range ranges {
fmt.Fprintln(buf, r.SQL("hash"))
}
if strings.TrimSpace(buf.String()) != strings.TrimSpace(exp) {
t.Log(buf.String())
t.Error("unexpected output")
}
}

View File

@@ -1206,6 +1206,47 @@ func TestOpenSpecialName(t *testing.T) {
db.Close()
}
func TestBlocksInserted(t *testing.T) {
// Verifies that blocks are inserted after syncing a file
t.Parallel()
sdb, err := Open(t.TempDir())
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := sdb.Close(); err != nil {
t.Fatal(err)
}
})
// Add remote file
files := []protocol.FileInfo{genFile("test1", 100, 1)}
if err := sdb.Update(folderID, protocol.DeviceID{42}, files); err != nil {
t.Fatal(err)
}
// Add the same file locally
if err := sdb.Update(folderID, protocol.LocalDeviceID, files); err != nil {
t.Fatal(err)
}
// Verify all the blocks are here
for i, block := range files[0].Blocks {
bs, err := itererr.Collect(sdb.AllLocalBlocksWithHash(folderID, block.Hash))
if err != nil {
t.Fatal(err)
}
if len(bs) == 0 {
t.Error("missing blocks for", i)
}
}
}
func mustCollect[T any](t *testing.T) func(it iter.Seq[T], errFn func() error) []T {
t.Helper()
return func(it iter.Seq[T], errFn func() error) []T {

View File

@@ -84,7 +84,7 @@ func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND NOT EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND f.version = g.version AND d.device_id = ?
WHERE f.name_idx = g.name_idx AND f.version_idx = g.version_idx AND d.device_id = ?
)
GROUP BY g.type, g.local_flags, g.deleted
@@ -94,7 +94,7 @@ func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
WHERE f.name_idx = g.name_idx AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
)
GROUP BY g.type, g.local_flags, g.deleted
`).Select(&res, device.String(),

View File

@@ -27,7 +27,8 @@ func (s *folderDB) GetGlobalFile(file string) (protocol.FileInfo, bool, error) {
SELECT fi.fiprotobuf, bl.blprotobuf FROM fileinfos fi
INNER JOIN files f on fi.sequence = f.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash
WHERE f.name = ? AND f.local_flags & {{.FlagLocalGlobal}} != 0
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE n.name = ? AND f.local_flags & {{.FlagLocalGlobal}} != 0
`).Get(&ind, file)
if errors.Is(err, sql.ErrNoRows) {
return protocol.FileInfo{}, false, nil
@@ -49,8 +50,9 @@ func (s *folderDB) GetGlobalAvailability(file string) ([]protocol.DeviceID, erro
err := s.stmt(`
SELECT d.device_id FROM files f
INNER JOIN devices d ON d.idx = f.device_idx
INNER JOIN files g ON g.version = f.version AND g.name = f.name
WHERE g.name = ? AND g.local_flags & {{.FlagLocalGlobal}} != 0 AND f.device_idx != {{.LocalDeviceIdx}}
INNER JOIN files g ON g.version_idx = f.version_idx AND g.name_idx = f.name_idx
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE n.name = ? AND g.local_flags & {{.FlagLocalGlobal}} != 0 AND f.device_idx != {{.LocalDeviceIdx}}
ORDER BY d.device_id
`).Select(&devStrs, file)
if errors.Is(err, sql.ErrNoRows) {
@@ -74,9 +76,10 @@ func (s *folderDB) GetGlobalAvailability(file string) ([]protocol.DeviceID, erro
func (s *folderDB) AllGlobalFiles() (iter.Seq[db.FileMetadata], func() error) {
it, errFn := iterStructs[db.FileMetadata](s.stmt(`
SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
SELECT f.sequence, n.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE f.local_flags & {{.FlagLocalGlobal}} != 0
ORDER BY f.name
ORDER BY n.name
`).Queryx())
return itererr.Map(it, errFn, func(m db.FileMetadata) (db.FileMetadata, error) {
m.Name = osutil.NativeFilename(m.Name)
@@ -93,9 +96,10 @@ func (s *folderDB) AllGlobalFilesPrefix(prefix string) (iter.Seq[db.FileMetadata
end := prefixEnd(prefix)
it, errFn := iterStructs[db.FileMetadata](s.stmt(`
SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
WHERE f.name >= ? AND f.name < ? AND f.local_flags & {{.FlagLocalGlobal}} != 0
ORDER BY f.name
SELECT f.sequence, n.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE n.name >= ? AND n.name < ? AND f.local_flags & {{.FlagLocalGlobal}} != 0
ORDER BY n.name
`).Queryx(prefix, end))
return itererr.Map(it, errFn, func(m db.FileMetadata) (db.FileMetadata, error) {
m.Name = osutil.NativeFilename(m.Name)
@@ -109,7 +113,7 @@ func (s *folderDB) AllNeededGlobalFiles(device protocol.DeviceID, order config.P
case config.PullOrderRandom:
selectOpts = "ORDER BY RANDOM()"
case config.PullOrderAlphabetic:
selectOpts = "ORDER BY g.name ASC"
selectOpts = "ORDER BY n.name ASC"
case config.PullOrderSmallestFirst:
selectOpts = "ORDER BY g.size ASC"
case config.PullOrderLargestFirst:
@@ -137,9 +141,10 @@ func (s *folderDB) AllNeededGlobalFiles(device protocol.DeviceID, order config.P
func (s *folderDB) neededGlobalFilesLocal(selectOpts string) (iter.Seq[protocol.FileInfo], func() error) {
// Select all the non-ignored files with the need bit set.
it, errFn := iterStructs[indirectFI](s.stmt(`
SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi
SELECT fi.fiprotobuf, bl.blprotobuf, n.name, g.size, g.modified FROM fileinfos fi
INNER JOIN files g on fi.sequence = g.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash
INNER JOIN file_names n ON g.name_idx = n.idx
WHERE g.local_flags & {{.FlagLocalIgnored}} = 0 AND g.local_flags & {{.FlagLocalNeeded}} != 0
` + selectOpts).Queryx())
return itererr.Map(it, errFn, indirectFI.FileInfo)
@@ -155,24 +160,26 @@ func (s *folderDB) neededGlobalFilesRemote(device protocol.DeviceID, selectOpts
// non-deleted and valid remote file (of any version)
it, errFn := iterStructs[indirectFI](s.stmt(`
SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi
SELECT fi.fiprotobuf, bl.blprotobuf, n.name, g.size, g.modified FROM fileinfos fi
INNER JOIN files g on fi.sequence = g.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash
INNER JOIN file_names n ON g.name_idx = n.idx
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND NOT EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND f.version = g.version AND d.device_id = ?
WHERE f.name_idx = g.name_idx AND f.version_idx = g.version_idx AND d.device_id = ?
)
UNION ALL
SELECT fi.fiprotobuf, bl.blprotobuf, g.name, g.size, g.modified FROM fileinfos fi
SELECT fi.fiprotobuf, bl.blprotobuf, n.name, g.size, g.modified FROM fileinfos fi
INNER JOIN files g on fi.sequence = g.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = g.blocklist_hash
INNER JOIN file_names n ON g.name_idx = n.idx
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
WHERE f.name_idx = g.name_idx AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
)
`+selectOpts).Queryx(
device.String(),

View File

@@ -32,7 +32,8 @@ func (s *folderDB) GetDeviceFile(device protocol.DeviceID, file string) (protoco
INNER JOIN files f on fi.sequence = f.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash
INNER JOIN devices d ON f.device_idx = d.idx
WHERE d.device_id = ? AND f.name = ?
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE d.device_id = ? AND n.name = ?
`).Get(&ind, device.String(), file)
if errors.Is(err, sql.ErrNoRows) {
return protocol.FileInfo{}, false, nil
@@ -87,14 +88,16 @@ func (s *folderDB) AllLocalFilesWithPrefix(device protocol.DeviceID, prefix stri
INNER JOIN files f on fi.sequence = f.sequence
LEFT JOIN blocklists bl ON bl.blocklist_hash = f.blocklist_hash
INNER JOIN devices d ON d.idx = f.device_idx
WHERE d.device_id = ? AND f.name >= ? AND f.name < ?
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE d.device_id = ? AND n.name >= ? AND n.name < ?
`, device.String(), prefix, end))
return itererr.Map(it, errFn, indirectFI.FileInfo)
}
func (s *folderDB) AllLocalFilesWithBlocksHash(h []byte) (iter.Seq[db.FileMetadata], func() error) {
return iterStructs[db.FileMetadata](s.stmt(`
SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
SELECT f.sequence, n.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags FROM files f
INNER JOIN file_names n ON f.name_idx = n.idx
WHERE f.device_idx = {{.LocalDeviceIdx}} AND f.blocklist_hash = ?
`).Queryx(h))
}
@@ -104,7 +107,8 @@ func (s *folderDB) AllLocalBlocksWithHash(hash []byte) (iter.Seq[db.BlockMapEntr
// & blocklists is deferred (garbage collected) while the files list is
// not. This filters out blocks that are in fact deleted.
return iterStructs[db.BlockMapEntry](s.stmt(`
SELECT f.blocklist_hash as blocklisthash, b.idx as blockindex, b.offset, b.size, f.name as filename FROM files f
SELECT f.blocklist_hash as blocklisthash, b.idx as blockindex, b.offset, b.size, n.name as filename FROM files f
INNER JOIN file_names n ON f.name_idx = n.idx
LEFT JOIN blocks b ON f.blocklist_hash = b.blocklist_hash
WHERE f.device_idx = {{.LocalDeviceIdx}} AND b.hash = ?
`).Queryx(hash))
@@ -170,10 +174,12 @@ func (s *folderDB) DebugFilePattern(out io.Writer, name string) error {
}
name = "%" + name + "%"
res := itererr.Zip(iterStructs[hashFileMetadata](s.stmt(`
SELECT f.sequence, f.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags, f.version, f.blocklist_hash as blocklisthash, d.device_id as deviceid FROM files f
SELECT f.sequence, n.name, f.type, f.modified as modnanos, f.size, f.deleted, f.local_flags as localflags, v.version, f.blocklist_hash as blocklisthash, d.device_id as deviceid FROM files f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name LIKE ?
ORDER BY f.name, f.device_idx
INNER JOIN file_names n ON n.idx = f.name_idx
INNER JOIN file_versions v ON v.idx = f.version_idx
WHERE n.name LIKE ?
ORDER BY n.name, f.device_idx
`).Queryx(name)))
delMap := map[bool]string{

View File

@@ -95,16 +95,13 @@ func openFolderDBForMigration(folder, path string, deleteRetention time.Duration
func (s *folderDB) deviceIdxLocked(deviceID protocol.DeviceID) (int64, error) {
devStr := deviceID.String()
if _, err := s.stmt(`
INSERT OR IGNORE INTO devices(device_id)
VALUES (?)
`).Exec(devStr); err != nil {
return 0, wrap(err)
}
var idx int64
if err := s.stmt(`
SELECT idx FROM devices
WHERE device_id = ?
INSERT INTO devices(device_id)
VALUES (?)
ON CONFLICT(device_id) DO UPDATE
SET device_id = excluded.device_id
RETURNING idx
`).Get(&idx, devStr); err != nil {
return 0, wrap(err)
}

View File

@@ -46,9 +46,33 @@ func (s *folderDB) Update(device protocol.DeviceID, fs []protocol.FileInfo) erro
defer tx.Rollback() //nolint:errcheck
txp := &txPreparedStmts{Tx: tx}
//nolint:sqlclosecheck
insertNameStmt, err := txp.Preparex(`
INSERT INTO file_names(name)
VALUES (?)
ON CONFLICT(name) DO UPDATE
SET name = excluded.name
RETURNING idx
`)
if err != nil {
return wrap(err, "prepare insert name")
}
//nolint:sqlclosecheck
insertVersionStmt, err := txp.Preparex(`
INSERT INTO file_versions (version)
VALUES (?)
ON CONFLICT(version) DO UPDATE
SET version = excluded.version
RETURNING idx
`)
if err != nil {
return wrap(err, "prepare insert version")
}
//nolint:sqlclosecheck
insertFileStmt, err := txp.Preparex(`
INSERT OR REPLACE INTO files (device_idx, remote_sequence, name, type, modified, size, version, deleted, local_flags, blocklist_hash)
INSERT OR REPLACE INTO files (device_idx, remote_sequence, type, modified, size, deleted, local_flags, blocklist_hash, name_idx, version_idx)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING sequence
`)
@@ -102,8 +126,19 @@ func (s *folderDB) Update(device protocol.DeviceID, fs []protocol.FileInfo) erro
prevRemoteSeq = f.Sequence
remoteSeq = &f.Sequence
}
var nameIdx int64
if err := insertNameStmt.Get(&nameIdx, f.Name); err != nil {
return wrap(err, "insert name")
}
var versionIdx int64
if err := insertVersionStmt.Get(&versionIdx, f.Version.String()); err != nil {
return wrap(err, "insert version")
}
var localSeq int64
if err := insertFileStmt.Get(&localSeq, deviceIdx, remoteSeq, f.Name, f.Type, f.ModTime().UnixNano(), f.Size, f.Version.String(), f.IsDeleted(), f.LocalFlags, blockshash); err != nil {
if err := insertFileStmt.Get(&localSeq, deviceIdx, remoteSeq, f.Type, f.ModTime().UnixNano(), f.Size, f.IsDeleted(), f.LocalFlags, blockshash, nameIdx, versionIdx); err != nil {
return wrap(err, "insert file")
}
@@ -114,9 +149,9 @@ func (s *folderDB) Update(device protocol.DeviceID, fs []protocol.FileInfo) erro
if err != nil {
return wrap(err, "marshal blocklist")
}
if res, err := insertBlockListStmt.Exec(f.BlocksHash, bs); err != nil {
if _, err := insertBlockListStmt.Exec(f.BlocksHash, bs); err != nil {
return wrap(err, "insert blocklist")
} else if aff, _ := res.RowsAffected(); aff != 0 && device == protocol.LocalDeviceID {
} else if device == protocol.LocalDeviceID {
// Insert all blocks
if err := s.insertBlocksLocked(txp, f.BlocksHash, f.Blocks); err != nil {
return wrap(err, "insert blocks")
@@ -246,7 +281,9 @@ func (s *folderDB) DropFilesNamed(device protocol.DeviceID, names []string) erro
query, args, err := sqlx.In(`
DELETE FROM files
WHERE device_idx = ? AND name IN (?)
WHERE device_idx = ? AND name_idx IN (
SELECT idx FROM file_names WHERE name IN (?)
)
`, deviceIdx, names)
if err != nil {
return wrap(err)
@@ -299,12 +336,13 @@ func (s *folderDB) recalcGlobalForFolder(txp *txPreparedStmts) error {
// recalculate.
//nolint:sqlclosecheck
namesStmt, err := txp.Preparex(`
SELECT f.name FROM files f
SELECT n.name FROM files f
INNER JOIN file_names n ON n.idx = f.name_idx
WHERE NOT EXISTS (
SELECT 1 FROM files g
WHERE g.name = f.name AND g.local_flags & ? != 0
WHERE g.name_idx = f.name_idx AND g.local_flags & ? != 0
)
GROUP BY name
GROUP BY n.name
`)
if err != nil {
return wrap(err)
@@ -329,11 +367,13 @@ func (s *folderDB) recalcGlobalForFolder(txp *txPreparedStmts) error {
func (s *folderDB) recalcGlobalForFile(txp *txPreparedStmts, file string) error {
//nolint:sqlclosecheck
selStmt, err := txp.Preparex(`
SELECT name, device_idx, sequence, modified, version, deleted, local_flags FROM files
WHERE name = ?
SELECT n.name, f.device_idx, f.sequence, f.modified, v.version, f.deleted, f.local_flags FROM files f
INNER JOIN file_versions v ON v.idx = f.version_idx
INNER JOIN file_names n ON n.idx = f.name_idx
WHERE n.name = ?
`)
if err != nil {
return wrap(err)
return wrap(err, "prepare select")
}
es, err := itererr.Collect(iterStructs[fileRow](selStmt.Queryx(file)))
if err != nil {
@@ -389,10 +429,10 @@ func (s *folderDB) recalcGlobalForFile(txp *txPreparedStmts, file string) error
//nolint:sqlclosecheck
upStmt, err = txp.Preparex(`
UPDATE files SET local_flags = local_flags & ?
WHERE name = ? AND sequence != ? AND local_flags & ? != 0
WHERE name_idx = (SELECT idx FROM file_names WHERE name = ?) AND sequence != ? AND local_flags & ? != 0
`)
if err != nil {
return wrap(err)
return wrap(err, "prepare update")
}
if _, err := upStmt.Exec(^(protocol.FlagLocalNeeded | protocol.FlagLocalGlobal), global.Name, global.Sequence, protocol.FlagLocalNeeded|protocol.FlagLocalGlobal); err != nil {
return wrap(err)

View File

@@ -0,0 +1,53 @@
-- Copyright (C) 2025 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/.
-- Grab all unique names into the names table
INSERT INTO file_names (idx, name) SELECT DISTINCT null, name FROM files
;
-- Grab all unique versions into the versions table
INSERT INTO file_versions (idx, version) SELECT DISTINCT null, version FROM files
;
-- Create the new files table
DROP TABLE IF EXISTS files_v5
;
CREATE TABLE files_v5 (
device_idx INTEGER NOT NULL,
sequence INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
remote_sequence INTEGER,
name_idx INTEGER NOT NULL, -- changed
type INTEGER NOT NULL,
modified INTEGER NOT NULL,
size INTEGER NOT NULL,
version_idx INTEGER NOT NULL, -- changed
deleted INTEGER NOT NULL,
local_flags INTEGER NOT NULL,
blocklist_hash BLOB,
FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE,
FOREIGN KEY(name_idx) REFERENCES file_names(idx), -- added
FOREIGN KEY(version_idx) REFERENCES file_versions(idx) -- added
) STRICT
;
-- Populate the new files table and move it in place
INSERT INTO files_v5
SELECT f.device_idx, f.sequence, f.remote_sequence, n.idx as name_idx, f.type, f.modified, f.size, v.idx as version_idx, f.deleted, f.local_flags, f.blocklist_hash
FROM files f
INNER JOIN file_names n ON n.name = f.name
INNER JOIN file_versions v ON v.version = f.version
;
DROP TABLE files
;
ALTER TABLE files_v5 RENAME TO files
;

View File

@@ -25,15 +25,27 @@ CREATE TABLE IF NOT EXISTS files (
device_idx INTEGER NOT NULL, -- actual device ID or LocalDeviceID
sequence INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- our local database sequence, for each and every entry
remote_sequence INTEGER, -- remote device's sequence number, null for local or synthetic entries
name TEXT NOT NULL COLLATE BINARY,
name_idx INTEGER NOT NULL,
type INTEGER NOT NULL, -- protocol.FileInfoType
modified INTEGER NOT NULL, -- Unix nanos
size INTEGER NOT NULL,
version TEXT NOT NULL COLLATE BINARY,
version_idx INTEGER NOT NULL,
deleted INTEGER NOT NULL, -- boolean
local_flags INTEGER NOT NULL,
blocklist_hash BLOB, -- null when there are no blocks
FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE
FOREIGN KEY(device_idx) REFERENCES devices(idx) ON DELETE CASCADE,
FOREIGN KEY(name_idx) REFERENCES file_names(idx),
FOREIGN KEY(version_idx) REFERENCES file_versions(idx)
) STRICT
;
CREATE TABLE IF NOT EXISTS file_names (
idx INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL UNIQUE COLLATE BINARY
) STRICT
;
CREATE TABLE IF NOT EXISTS file_versions (
idx INTEGER NOT NULL PRIMARY KEY,
version TEXT NOT NULL UNIQUE COLLATE BINARY
) STRICT
;
-- FileInfos store the actual protobuf object. We do this separately to keep
@@ -49,11 +61,17 @@ CREATE UNIQUE INDEX IF NOT EXISTS files_remote_sequence ON files (device_idx, re
WHERE remote_sequence IS NOT NULL
;
-- There can be only one file per folder, device, and name
CREATE UNIQUE INDEX IF NOT EXISTS files_device_name ON files (device_idx, name)
;
-- We want to be able to look up & iterate files based on just folder and name
CREATE INDEX IF NOT EXISTS files_name_only ON files (name)
CREATE UNIQUE INDEX IF NOT EXISTS files_device_name ON files (device_idx, name_idx)
;
-- We want to be able to look up & iterate files based on blocks hash
CREATE INDEX IF NOT EXISTS files_blocklist_hash_only ON files (blocklist_hash, device_idx) WHERE blocklist_hash IS NOT NULL
;
-- We need to look by name_idx or version_idx for garbage collection.
-- This will fail pre-migration for v4 schemas, which is fine.
-- syncthing:ignore-failure
CREATE INDEX IF NOT EXISTS files_name_idx_only ON files (name_idx)
;
-- This will fail pre-migration for v4 schemas, which is fine.
-- syncthing:ignore-failure
CREATE INDEX IF NOT EXISTS files_version_idx_only ON files (version_idx)
;

View File

@@ -944,28 +944,29 @@ type FileInfo struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
ModifiedS int64 `protobuf:"varint,5,opt,name=modified_s,json=modifiedS,proto3" json:"modified_s,omitempty"`
ModifiedBy uint64 `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3" json:"modified_by,omitempty"`
Version *Vector `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
Blocks []*BlockInfo `protobuf:"bytes,16,rep,name=blocks,proto3" json:"blocks,omitempty"`
SymlinkTarget []byte `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
BlocksHash []byte `protobuf:"bytes,18,opt,name=blocks_hash,json=blocksHash,proto3" json:"blocks_hash,omitempty"`
Encrypted []byte `protobuf:"bytes,19,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
Type FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=bep.FileInfoType" json:"type,omitempty"`
Permissions uint32 `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
BlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
Platform *PlatformData `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
ModifiedS int64 `protobuf:"varint,5,opt,name=modified_s,json=modifiedS,proto3" json:"modified_s,omitempty"`
ModifiedBy uint64 `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3" json:"modified_by,omitempty"`
Version *Vector `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
Blocks []*BlockInfo `protobuf:"bytes,16,rep,name=blocks,proto3" json:"blocks,omitempty"`
SymlinkTarget []byte `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
BlocksHash []byte `protobuf:"bytes,18,opt,name=blocks_hash,json=blocksHash,proto3" json:"blocks_hash,omitempty"`
PreviousBlocksHash []byte `protobuf:"bytes,20,opt,name=previous_blocks_hash,json=previousBlocksHash,proto3" json:"previous_blocks_hash,omitempty"`
Encrypted []byte `protobuf:"bytes,19,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
Type FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=bep.FileInfoType" json:"type,omitempty"`
Permissions uint32 `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
BlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
Platform *PlatformData `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,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"`
// The version_hash is an implementation detail and not part of the wire
// format.
// format. It is used in the old database format.
VersionHash []byte `protobuf:"bytes,1001,opt,name=version_hash,json=versionHash,proto3" json:"version_hash,omitempty"`
// The time when the inode was last changed (i.e., permissions, xattrs
// etc changed). This is host-local, not sent over the wire.
@@ -1071,6 +1072,13 @@ func (x *FileInfo) GetBlocksHash() []byte {
return nil
}
func (x *FileInfo) GetPreviousBlocksHash() []byte {
if x != nil {
return x.PreviousBlocksHash
}
return nil
}
func (x *FileInfo) GetEncrypted() []byte {
if x != nil {
return x.Encrypted
@@ -2084,7 +2092,7 @@ var file_bep_bep_proto_rawDesc = []byte{
0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x23, 0x0a,
0x0d, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e,
0x63, 0x65, 0x22, 0xfe, 0x05, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12,
0x63, 0x65, 0x22, 0xb0, 0x06, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66,
@@ -2102,198 +2110,201 @@ var file_bep_bep_proto_rawDesc = []byte{
0x72, 0x67, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x79, 0x6d, 0x6c,
0x69, 0x6e, 0x6b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c,
0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6e, 0x73,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64,
0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0e, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f,
0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18,
0xe8, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c, 0x61,
0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61,
0x73, 0x68, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x5f,
0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0xea, 0x07, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0d, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x73, 0x12,
0x37, 0x0a, 0x17, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72,
0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0xeb, 0x07, 0x20, 0x01, 0x28,
0x05, 0x52, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61,
0x69, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x07, 0x20,
0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e,
0x6e, 0x6f, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f,
0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04,
0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x32, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x12, 0x28, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x2f, 0x0a, 0x07, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xfd, 0x01, 0x0a, 0x0c,
0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x04,
0x75, 0x6e, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62, 0x65, 0x70,
0x2e, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x12,
0x2a, 0x0a, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x10, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x61,
0x74, 0x61, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x6c,
0x69, 0x6e, 0x75, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70,
0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x75,
0x78, 0x12, 0x26, 0x0a, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74,
0x61, 0x52, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x66, 0x72, 0x65,
0x65, 0x62, 0x73, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70,
0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x66, 0x72, 0x65, 0x65,
0x62, 0x73, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x18, 0x06, 0x20,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x72,
0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x68, 0x61,
0x73, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f,
0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09,
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79,
0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46,
0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70,
0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f,
0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,
0x65, 0x64, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69,
0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53,
0x69, 0x7a, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18,
0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x50, 0x6c, 0x61, 0x74,
0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f,
0x72, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67,
0x73, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46,
0x6c, 0x61, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x68, 0x61, 0x73, 0x68, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x6f, 0x64,
0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0xea, 0x07, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0d, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e,
0x73, 0x12, 0x37, 0x0a, 0x17, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0xeb, 0x07, 0x20,
0x01, 0x28, 0x05, 0x52, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54,
0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18,
0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x25,
0x0a, 0x0e, 0x6e, 0x6f, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12,
0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69,
0x7a, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0x32, 0x0a, 0x06, 0x56, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x12, 0x28, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x65, 0x72, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x2f, 0x0a, 0x07,
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xfd, 0x01,
0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21,
0x0a, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62,
0x65, 0x70, 0x2e, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x75, 0x6e, 0x69,
0x78, 0x12, 0x2a, 0x0a, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73,
0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x12, 0x24, 0x0a,
0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62,
0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x6c, 0x69,
0x6e, 0x75, 0x78, 0x12, 0x26, 0x0a, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44,
0x61, 0x74, 0x61, 0x52, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x22, 0x6c, 0x0a, 0x08, 0x55,
0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,
0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x04,
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x0b, 0x57, 0x69, 0x6e,
0x64, 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e, 0x65,
0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x77,
0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x69, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x2f, 0x0a,
0x09, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06, 0x78, 0x61,
0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x62, 0x65, 0x70,
0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x52, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, 0x31,
0x0a, 0x05, 0x58, 0x61, 0x74, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x22, 0xcd, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a,
0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66,
0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66,
0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65,
0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,
0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x72, 0x6f,
0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79,
0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x6f, 0x18, 0x09, 0x20, 0x01,
0x28, 0x05, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x6f, 0x4a, 0x04, 0x08, 0x08, 0x10,
0x09, 0x22, 0x52, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a,
0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74,
0x61, 0x12, 0x22, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52,
0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x65, 0x0a, 0x10, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61,
0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65,
0x72, 0x12, 0x39, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77,
0x61, 0x74, 0x61, 0x52, 0x06, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x66,
0x72, 0x65, 0x65, 0x62, 0x73, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62,
0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x66, 0x72,
0x65, 0x65, 0x62, 0x73, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x18,
0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74,
0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x22, 0x6c, 0x0a,
0x08, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f,
0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x0b, 0x57,
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x77,
0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x6f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x5f, 0x69, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22,
0x2f, 0x0a, 0x09, 0x58, 0x61, 0x74, 0x74, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x06,
0x78, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x62,
0x65, 0x70, 0x2e, 0x58, 0x61, 0x74, 0x74, 0x72, 0x52, 0x06, 0x78, 0x61, 0x74, 0x74, 0x72, 0x73,
0x22, 0x31, 0x0a, 0x05, 0x58, 0x61, 0x74, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x22, 0xcd, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12,
0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f,
0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66,
0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18,
0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x66,
0x72, 0x6f, 0x6d, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61,
0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x6f, 0x18, 0x09,
0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x6f, 0x4a, 0x04, 0x08,
0x08, 0x10, 0x09, 0x22, 0x52, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12,
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64,
0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64,
0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x65, 0x0a, 0x10, 0x44, 0x6f, 0x77, 0x6e, 0x6c,
0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x66,
0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44,
0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xe5,
0x01, 0x0a, 0x1a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a,
0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x23, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77,
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a,
0x1a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f,
0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x23, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c,
0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x56, 0x65, 0x63,
0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0d,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e,
0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73,
0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x53, 0x69, 0x7a, 0x65, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x1f, 0x0a, 0x05,
0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2a, 0xed, 0x01,
0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a,
0x1b, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c,
0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x16,
0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49,
0x4e, 0x44, 0x45, 0x58, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47,
0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x5f, 0x55, 0x50, 0x44,
0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x03, 0x12,
0x19, 0x0a, 0x15, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, 0x12, 0x22, 0x0a, 0x1e, 0x4d, 0x45,
0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c,
0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x05, 0x12, 0x15,
0x0a, 0x11, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50,
0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x07, 0x2a, 0x4f, 0x0a,
0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43,
0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10,
0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4f, 0x4d,
0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, 0x5a, 0x34, 0x10, 0x01, 0x2a, 0x56,
0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a,
0x14, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54,
0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d, 0x50, 0x52,
0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x16,
0x0a, 0x12, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c,
0x57, 0x41, 0x59, 0x53, 0x10, 0x02, 0x2a, 0x86, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x6c, 0x64, 0x65,
0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56,
0x45, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x12, 0x1c,
0x0a, 0x18, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45,
0x43, 0x45, 0x49, 0x56, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d,
0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x45,
0x49, 0x56, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x2a,
0x51, 0x0a, 0x10, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x1a, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x53, 0x54,
0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e,
0x47, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x53, 0x54,
0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44,
0x10, 0x01, 0x2a, 0xb0, 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54,
0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18,
0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44,
0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1b, 0x46, 0x49,
0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d,
0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x1a, 0x02, 0x08, 0x01, 0x12,
0x28, 0x0a, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50,
0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54,
0x4f, 0x52, 0x59, 0x10, 0x03, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x4c,
0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c,
0x49, 0x4e, 0x4b, 0x10, 0x04, 0x2a, 0x76, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f,
0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45,
0x5f, 0x4e, 0x4f, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x45,
0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49,
0x43, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44,
0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x53, 0x55, 0x43, 0x48, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02,
0x12, 0x1b, 0x0a, 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49,
0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x7e, 0x0a,
0x1e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f,
0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
0x2d, 0x0a, 0x29, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44,
0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x2d,
0x0a, 0x29, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f,
0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x01, 0x42, 0x70, 0x0a,
0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x65, 0x70, 0x42, 0x08, 0x42, 0x65, 0x70, 0x50, 0x72, 0x6f,
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74,
0x68, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65,
0x6e, 0x2f, 0x62, 0x65, 0x70, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x42, 0x65,
0x70, 0xca, 0x02, 0x03, 0x42, 0x65, 0x70, 0xe2, 0x02, 0x0f, 0x42, 0x65, 0x70, 0x5c, 0x47, 0x50,
0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x42, 0x65, 0x70, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54,
0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x56,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x27,
0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x1f,
0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f,
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2a,
0xed, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
0x1f, 0x0a, 0x1b, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00,
0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x45, 0x53, 0x53,
0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x5f, 0x55,
0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x53, 0x53, 0x41,
0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10,
0x03, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50,
0x45, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, 0x12, 0x22, 0x0a, 0x1e,
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x57,
0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x05,
0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x4d, 0x45, 0x53, 0x53, 0x41,
0x47, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x07, 0x2a,
0x4f, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e,
0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43,
0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, 0x5a, 0x34, 0x10, 0x01,
0x2a, 0x56, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d,
0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4d,
0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x45, 0x56, 0x45, 0x52, 0x10, 0x01,
0x12, 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f,
0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x02, 0x2a, 0x86, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x4f, 0x4c, 0x44, 0x45,
0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x43, 0x45,
0x49, 0x56, 0x45, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01,
0x12, 0x1c, 0x0a, 0x18, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x21,
0x0a, 0x1d, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45,
0x43, 0x45, 0x49, 0x56, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54, 0x45, 0x44, 0x10,
0x03, 0x2a, 0x51, 0x0a, 0x10, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x52,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x1a, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f,
0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x55, 0x4e, 0x4e,
0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x5f,
0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x55, 0x53,
0x45, 0x44, 0x10, 0x01, 0x2a, 0xb0, 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66,
0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e,
0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c,
0x0a, 0x18, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1b,
0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53,
0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x02, 0x1a, 0x02, 0x08,
0x01, 0x12, 0x28, 0x0a, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54,
0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x44, 0x49, 0x52, 0x45,
0x43, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x03, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46,
0x49, 0x4c, 0x45, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59,
0x4d, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x04, 0x2a, 0x76, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72,
0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f,
0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x16, 0x0a,
0x12, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x47, 0x45, 0x4e, 0x45,
0x52, 0x49, 0x43, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43,
0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x53, 0x55, 0x43, 0x48, 0x5f, 0x46, 0x49, 0x4c, 0x45,
0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45,
0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a,
0x7e, 0x0a, 0x1e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x50,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70,
0x65, 0x12, 0x2d, 0x0a, 0x29, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f,
0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41,
0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x10, 0x00,
0x12, 0x2d, 0x0a, 0x29, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41,
0x44, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54,
0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x01, 0x42,
0x70, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x65, 0x70, 0x42, 0x08, 0x42, 0x65, 0x70, 0x50,
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x73, 0x79, 0x6e,
0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
0x67, 0x65, 0x6e, 0x2f, 0x62, 0x65, 0x70, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x03,
0x42, 0x65, 0x70, 0xca, 0x02, 0x03, 0x42, 0x65, 0x70, 0xe2, 0x02, 0x0f, 0x42, 0x65, 0x70, 0x5c,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x42, 0x65,
0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -28,20 +28,21 @@ type FileInfoTruncated struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
ModifiedS int64 `protobuf:"varint,5,opt,name=modified_s,json=modifiedS,proto3" json:"modified_s,omitempty"`
ModifiedBy uint64 `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3" json:"modified_by,omitempty"`
Version *bep.Vector `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
SymlinkTarget []byte `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
BlocksHash []byte `protobuf:"bytes,18,opt,name=blocks_hash,json=blocksHash,proto3" json:"blocks_hash,omitempty"`
Encrypted []byte `protobuf:"bytes,19,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
Type bep.FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=bep.FileInfoType" json:"type,omitempty"`
Permissions uint32 `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
BlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
Platform *bep.PlatformData `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
ModifiedS int64 `protobuf:"varint,5,opt,name=modified_s,json=modifiedS,proto3" json:"modified_s,omitempty"`
ModifiedBy uint64 `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3" json:"modified_by,omitempty"`
Version *bep.Vector `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"`
SymlinkTarget []byte `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"`
BlocksHash []byte `protobuf:"bytes,18,opt,name=blocks_hash,json=blocksHash,proto3" json:"blocks_hash,omitempty"`
PreviousBlocksHash []byte `protobuf:"bytes,20,opt,name=previous_blocks_hash,json=previousBlocksHash,proto3" json:"previous_blocks_hash,omitempty"`
Encrypted []byte `protobuf:"bytes,19,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
Type bep.FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=bep.FileInfoType" json:"type,omitempty"`
Permissions uint32 `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"`
BlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"`
Platform *bep.PlatformData `protobuf:"bytes,14,opt,name=platform,proto3" json:"platform,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
@@ -147,6 +148,13 @@ func (x *FileInfoTruncated) GetBlocksHash() []byte {
return nil
}
func (x *FileInfoTruncated) GetPreviousBlocksHash() []byte {
if x != nil {
return x.PreviousBlocksHash
}
return nil
}
func (x *FileInfoTruncated) GetEncrypted() []byte {
if x != nil {
return x.Encrypted
@@ -747,7 +755,7 @@ var file_dbproto_structs_proto_rawDesc = []byte{
0x1a, 0x0d, 0x62, 0x65, 0x70, 0x2f, 0x62, 0x65, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0xe5, 0x05, 0x0a, 0x11, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, 0x75,
0x22, 0x97, 0x06, 0x0a, 0x11, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, 0x75,
0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69,
0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1d,
@@ -763,107 +771,110 @@ var file_dbproto_structs_proto_rawDesc = []byte{
0x67, 0x65, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x79, 0x6d, 0x6c, 0x69,
0x6e, 0x6b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69, 0x6c, 0x65,
0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20,
0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18,
0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e,
0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65,
0x12, 0x2d, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0e, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12,
0x20, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0xe8,
0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c, 0x61, 0x67,
0x73, 0x12, 0x22, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73,
0x68, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0xea, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0d, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x73, 0x12, 0x37,
0x0a, 0x17, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61,
0x69, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0xeb, 0x07, 0x20, 0x01, 0x28, 0x05,
0x52, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x69,
0x6c, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x64, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01,
0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6e,
0x6f, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x22, 0x91, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6c,
0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x62, 0x65, 0x70, 0x2e,
0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69,
0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3f, 0x0a, 0x0b,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e,
0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x33, 0x0a,
0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x06, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x65, 0x70,
0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x73, 0x22, 0x5c, 0x0a, 0x15, 0x49, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0xe9, 0x07, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68,
0x22, 0xe6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65,
0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12,
0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05,
0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74,
0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12,
0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08,
0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4e, 0x0a, 0x09, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x73, 0x53, 0x65, 0x74, 0x12, 0x27, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12,
0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x0e, 0x4f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05,
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62,
0x65, 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72,
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12,
0x29, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0x6e, 0x0a, 0x0e, 0x4f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x04,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x8c, 0x01, 0x0a, 0x0b, 0x63,
0x6f, 0x6d, 0x2e, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0c, 0x53, 0x74, 0x72, 0x75,
0x63, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67,
0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0xa2,
0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0xca,
0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0xe2, 0x02, 0x13, 0x44, 0x62, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x72, 0x65,
0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x68, 0x61, 0x73,
0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75,
0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x65,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x46, 0x69,
0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6e,
0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,
0x64, 0x4e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x69, 0x7a,
0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69,
0x7a, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x0e,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x65, 0x70, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66,
0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73,
0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c,
0x61, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x68,
0x61, 0x73, 0x68, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x6f, 0x64, 0x65,
0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0xea, 0x07, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0d, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x73,
0x12, 0x37, 0x0a, 0x17, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0xeb, 0x07, 0x20, 0x01,
0x28, 0x05, 0x52, 0x15, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72,
0x61, 0x69, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x07,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x25, 0x0a,
0x0e, 0x6e, 0x6f, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x22, 0x91, 0x01, 0x0a, 0x0b, 0x46,
0x69, 0x6c, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x62, 0x65,
0x70, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x65,
0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e,
0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x3f,
0x0a, 0x0b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, 0x0a,
0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x14, 0x2e, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22,
0x33, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x06,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62,
0x65, 0x70, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x73, 0x22, 0x5c, 0x0a, 0x15, 0x49, 0x6e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1f, 0x0a,
0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22,
0x0a, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0xe9,
0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x61,
0x73, 0x68, 0x22, 0xe6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a,
0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69,
0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b,
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01,
0x28, 0x05, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62,
0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65,
0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20,
0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x0a,
0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x4e, 0x0a, 0x09, 0x43,
0x6f, 0x75, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x74, 0x12, 0x27, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x62, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x0e,
0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x2e,
0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14,
0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c,
0x61, 0x62, 0x65, 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f,
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x22, 0x6e, 0x0a, 0x0e,
0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e,
0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x8c, 0x01, 0x0a,
0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0c, 0x53, 0x74,
0x72, 0x75, 0x63, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x64, 0x62, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0xca, 0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0xe2, 0x02, 0x13, 0x44, 0x62,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0xea, 0x02, 0x07, 0x44, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (

View File

@@ -1772,22 +1772,23 @@ func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
func fileIntfJSONMap(f protocol.FileInfo) map[string]interface{} {
out := map[string]interface{}{
"name": f.FileName(),
"type": f.FileType().String(),
"size": f.FileSize(),
"deleted": f.IsDeleted(),
"invalid": f.IsInvalid(),
"ignored": f.IsIgnored(),
"mustRescan": f.MustRescan(),
"noPermissions": !f.HasPermissionBits(),
"modified": f.ModTime(),
"modifiedBy": f.FileModifiedBy().String(),
"sequence": f.SequenceNo(),
"version": jsonVersionVector(f.FileVersion()),
"localFlags": f.FileLocalFlags(),
"platform": f.PlatformData(),
"inodeChange": f.InodeChangeTime(),
"blocksHash": f.FileBlocksHash(),
"name": f.FileName(),
"type": f.FileType().String(),
"size": f.FileSize(),
"deleted": f.IsDeleted(),
"invalid": f.IsInvalid(),
"ignored": f.IsIgnored(),
"mustRescan": f.MustRescan(),
"noPermissions": !f.HasPermissionBits(),
"modified": f.ModTime(),
"modifiedBy": f.FileModifiedBy().String(),
"sequence": f.SequenceNo(),
"version": jsonVersionVector(f.FileVersion()),
"localFlags": f.FileLocalFlags(),
"platform": f.PlatformData(),
"inodeChange": f.InodeChangeTime(),
"blocksHash": f.FileBlocksHash(),
"previousBlocksHash": f.PreviousBlocksHash,
}
if f.HasPermissionBits() {
out["permissions"] = fmt.Sprintf("%#o", f.FilePermissions())

View File

@@ -25,9 +25,10 @@ import (
)
const (
maxSessionLifetime = 7 * 24 * time.Hour
maxActiveSessions = 25
randomTokenLength = 64
maxSessionLifetime = 7 * 24 * time.Hour
maxActiveSessions = 25
randomTokenLength = 64
maxLoginRequestSize = 1 << 10 // one kibibyte for username+password
)
func emitLoginAttempt(success bool, username string, r *http.Request, evLogger events.Logger) {
@@ -182,7 +183,7 @@ func (m *basicAuthAndSessionMiddleware) passwordAuthHandler(w http.ResponseWrite
Password string
StayLoggedIn bool
}
if err := unmarshalTo(r.Body, &req); err != nil {
if err := unmarshalTo(http.MaxBytesReader(w, r.Body, maxLoginRequestSize), &req); err != nil {
l.Debugln("Failed to parse username and password:", err)
http.Error(w, "Failed to parse username and password.", http.StatusBadRequest)
return

View File

@@ -459,6 +459,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
defer cancel()
for {
select {
case <-ctx.Done():
@@ -466,9 +467,9 @@ func TestRecvOnlyRevertOwnID(t *testing.T) {
case <-sub.C():
if file, _ := m.testCurrentFolderFile(f.ID, name); file.Deleted {
t.Error("local file was deleted")
cancel()
return
} else if file.IsEquivalent(fi, f.modTimeWindow) {
cancel() // That's what we are waiting for
return // That's what we are waiting for
}
}
}

View File

@@ -602,7 +602,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
}
// Remove it to replace with the dir.
if !curFile.IsSymlink() && f.inConflict(curFile.Version, file.Version) {
if !curFile.IsSymlink() && file.InConflictWith(curFile) {
// The new file has been changed in conflict with the existing one. We
// should file it away as a conflict instead of just removing or
// archiving.
@@ -800,7 +800,7 @@ func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, s
}
// Remove it to replace with the symlink. This also handles the
// "change symlink type" path.
if !curFile.IsDirectory() && !curFile.IsSymlink() && f.inConflict(curFile.Version, file.Version) {
if !curFile.IsDirectory() && !curFile.IsSymlink() && file.InConflictWith(curFile) {
// The new file has been changed in conflict with the existing one. We
// should file it away as a conflict instead of just removing or
// archiving.
@@ -923,7 +923,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
}
switch {
case f.inConflict(cur.Version, file.Version) && !cur.IsSymlink():
case file.InConflictWith(cur) && !cur.IsSymlink():
// If the delete constitutes winning a conflict, we move the file to
// a conflict copy instead of doing the delete
err = f.inWritableDir(func(name string) error {
@@ -1652,7 +1652,7 @@ func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCu
return fmt.Errorf("checking existing file: %w", err)
}
if !curFile.IsDirectory() && !curFile.IsSymlink() && f.inConflict(curFile.Version, file.Version) {
if !curFile.IsDirectory() && !curFile.IsSymlink() && file.InConflictWith(curFile) {
// The new file has been changed in conflict with the existing one. We
// should file it away as a conflict instead of just removing or
// archiving.
@@ -1847,22 +1847,6 @@ func (f *sendReceiveFolder) pullScannerRoutine(scanChan <-chan string) {
}
}
func (f *sendReceiveFolder) inConflict(current, replacement protocol.Vector) bool {
if current.Concurrent(replacement) {
// Obvious case
return true
}
if replacement.Counter(f.shortID) > current.Counter(f.shortID) {
// The replacement file contains a higher version for ourselves than
// what we have. This isn't supposed to be possible, since it's only
// we who can increment that counter. We take it as a sign that
// something is wrong (our index may have been corrupted or removed)
// and flag it as a conflict.
return true
}
return false
}
func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan chan<- string) error {
if isConflict(name) {
f.sl.Info("Conflict on existing conflict copy; not copying again", slogutil.FilePath(name))

View File

@@ -2615,6 +2615,7 @@ func (m *model) generateClusterConfigRLocked(device protocol.DeviceID) (*protoco
protocolFolder.StopReason = protocol.FolderStopReasonPaused
}
nextDevice:
for _, folderDevice := range folderCfg.Devices {
deviceCfg, _ := m.cfg.Device(folderDevice.DeviceID)
@@ -2630,9 +2631,17 @@ func (m *model) generateClusterConfigRLocked(device protocol.DeviceID) (*protoco
if deviceCfg.DeviceID == m.id && hasEncryptionToken {
protocolDevice.EncryptionPasswordToken = encryptionToken
} else if folderDevice.EncryptionPassword != "" {
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(m.keyGen, folderCfg.ID, folderDevice.EncryptionPassword)
// For encrypted/untrusted devices we send the encryption
// token and prepare the password. We do not send any
// information about untrusted devices to _other_ devices,
// as they are not normal peers sharing the folder and we
// don't want things like the introducer features to kick in
// for them.
if folderDevice.DeviceID == device {
protocolDevice.EncryptionPasswordToken = protocol.PasswordToken(m.keyGen, folderCfg.ID, folderDevice.EncryptionPassword)
passwords[folderCfg.ID] = folderDevice.EncryptionPassword
} else {
continue nextDevice
}
}

View File

@@ -474,6 +474,84 @@ func TestClusterConfig(t *testing.T) {
}
}
func TestClusterConfigEncrypted(t *testing.T) {
t.Parallel()
cfg := config.New(device1)
cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
cfg.Devices = []config.DeviceConfiguration{
{
DeviceID: device1,
},
{
DeviceID: device2,
},
}
cfg.Folders = []config.FolderConfiguration{
{
FilesystemType: config.FilesystemTypeFake,
ID: "folder1",
Path: "testdata1",
Devices: []config.FolderDeviceConfiguration{
{DeviceID: device1, EncryptionPassword: "trololol"}, // not included, untrusted
{DeviceID: device2},
},
},
{
FilesystemType: config.FilesystemTypeFake,
ID: "folder2",
Path: "testdata2",
Devices: []config.FolderDeviceConfiguration{
{DeviceID: device1, EncryptionPassword: "trololol"}, // not included, untrusted
{DeviceID: device2, EncryptionPassword: "trololol"}, // included, is destinationd device
},
},
{
FilesystemType: config.FilesystemTypeFake,
ID: "folder3",
Path: "testdata3",
Devices: []config.FolderDeviceConfiguration{
{DeviceID: device1},
// should not be included, does not include device2
},
},
}
wrapper, cancel := newConfigWrapper(cfg)
defer cancel()
m := newModel(t, wrapper, myID, nil)
m.ServeBackground()
defer cleanupModel(m)
cm, _ := m.generateClusterConfig(device2)
if l := len(cm.Folders); l != 2 {
t.Fatalf("Incorrect number of folders %d != 2", l)
}
r := cm.Folders[0]
if r.ID != "folder1" {
t.Errorf("Incorrect folder %q != folder1", r.ID)
}
if l := len(r.Devices); l != 1 {
t.Errorf("Incorrect number of devices %d != 1", l)
}
if id := r.Devices[0].ID; id != device2 {
t.Errorf("Incorrect device ID %s != %s", id, device2)
}
r = cm.Folders[1]
if r.ID != "folder2" {
t.Errorf("Incorrect folder %q != folder2", r.ID)
}
if l := len(r.Devices); l != 1 {
t.Errorf("Incorrect number of devices %d != 1", l)
}
if id := r.Devices[0].ID; id != device2 {
t.Errorf("Incorrect device ID %s != %s", id, device2)
}
}
func TestIntroducer(t *testing.T) {
var introducedByAnyone protocol.DeviceID

View File

@@ -8,6 +8,7 @@
package osutil
import (
"os"
"path/filepath"
"strings"
"sync"
@@ -142,3 +143,21 @@ func IsDeleted(ffs fs.Filesystem, name string) bool {
}
return false
}
func DirSize(location string) int64 {
entries, err := os.ReadDir(location)
if err != nil {
return 0
}
var size int64
for _, entry := range entries {
fi, err := entry.Info()
if err != nil {
continue
}
size += fi.Size()
}
return size
}

View File

@@ -113,17 +113,18 @@ const (
)
type FileInfo struct {
Name string
Size int64
ModifiedS int64
ModifiedBy ShortID
Version Vector
Sequence int64
Blocks []BlockInfo
SymlinkTarget []byte
BlocksHash []byte
Encrypted []byte
Platform PlatformData
Name string
Size int64
ModifiedS int64
ModifiedBy ShortID
Version Vector
Sequence int64
Blocks []BlockInfo
SymlinkTarget []byte
BlocksHash []byte
PreviousBlocksHash []byte
Encrypted []byte
Platform PlatformData
Type FileInfoType
Permissions uint32
@@ -138,10 +139,6 @@ type FileInfo struct {
// the protocol.
LocalFlags FlagLocal
// The version_hash is an implementation detail and not part of the wire
// format.
VersionHash []byte
// The time when the inode was last changed (i.e., permissions, xattrs
// etc changed). This is host-local, not sent over the wire.
InodeChangeNs int64
@@ -165,34 +162,54 @@ func (f *FileInfo) ToWire(withInternalFields bool) *bep.FileInfo {
blocks[j] = b.ToWire()
}
w := &bep.FileInfo{
Name: f.Name,
Size: f.Size,
ModifiedS: f.ModifiedS,
ModifiedBy: uint64(f.ModifiedBy),
Version: f.Version.ToWire(),
Sequence: f.Sequence,
Blocks: blocks,
SymlinkTarget: f.SymlinkTarget,
BlocksHash: f.BlocksHash,
Encrypted: f.Encrypted,
Type: f.Type,
Permissions: f.Permissions,
ModifiedNs: f.ModifiedNs,
BlockSize: f.RawBlockSize,
Platform: f.Platform.toWire(),
Deleted: f.Deleted,
Invalid: f.IsInvalid(),
NoPermissions: f.NoPermissions,
Name: f.Name,
Size: f.Size,
ModifiedS: f.ModifiedS,
ModifiedBy: uint64(f.ModifiedBy),
Version: f.Version.ToWire(),
Sequence: f.Sequence,
Blocks: blocks,
SymlinkTarget: f.SymlinkTarget,
BlocksHash: f.BlocksHash,
PreviousBlocksHash: f.PreviousBlocksHash,
Encrypted: f.Encrypted,
Type: f.Type,
Permissions: f.Permissions,
ModifiedNs: f.ModifiedNs,
BlockSize: f.RawBlockSize,
Platform: f.Platform.toWire(),
Deleted: f.Deleted,
Invalid: f.IsInvalid(),
NoPermissions: f.NoPermissions,
}
if withInternalFields {
w.LocalFlags = uint32(f.LocalFlags)
w.VersionHash = f.VersionHash
w.InodeChangeNs = f.InodeChangeNs
w.EncryptionTrailerSize = int32(f.EncryptionTrailerSize)
}
return w
}
func (f *FileInfo) InConflictWith(previous FileInfo) bool {
if f.Version.GreaterEqual(previous.Version) {
// If the new file is strictly greater in the ordering than the
// existing file, it is not a conflict. If any counter has moved
// backwards, or different counters have increased independently,
// then the file is not greater but concurrent and we don't take
// this branch.
return false
}
if len(f.PreviousBlocksHash) == 0 || len(f.BlocksHash) == 0 {
// Don't have data to make a content determination, or the type has
// changed (file to directory, etc). Consider it a conflict.
return true
}
// If the new file is based on the old contents we have, it's not really
// a conflict.
return !bytes.Equal(f.PreviousBlocksHash, previous.BlocksHash)
}
// WinsConflict returns true if "f" is the one to choose when it is in
// conflict with "other".
func (f *FileInfo) WinsConflict(other FileInfo) bool {
@@ -259,6 +276,7 @@ type FileInfoWithoutBlocks interface {
// GetBlocks() []*bep.BlockInfo // not included
GetSymlinkTarget() []byte
GetBlocksHash() []byte
GetPreviousBlocksHash() []byte
GetEncrypted() []byte
GetType() FileInfoType
GetPermissions() uint32
@@ -266,7 +284,6 @@ type FileInfoWithoutBlocks interface {
GetBlockSize() int32
GetPlatform() *bep.PlatformData
GetLocalFlags() uint32
GetVersionHash() []byte
GetInodeChangeNs() int64
GetEncryptionTrailerSize() int32
GetDeleted() bool
@@ -280,31 +297,31 @@ func fileInfoFromWireWithBlocks(w FileInfoWithoutBlocks, blocks []BlockInfo) Fil
localFlags = FlagLocalRemoteInvalid
}
return FileInfo{
Name: w.GetName(),
Size: w.GetSize(),
ModifiedS: w.GetModifiedS(),
ModifiedBy: ShortID(w.GetModifiedBy()),
Version: VectorFromWire(w.GetVersion()),
Sequence: w.GetSequence(),
Blocks: blocks,
SymlinkTarget: w.GetSymlinkTarget(),
BlocksHash: w.GetBlocksHash(),
Encrypted: w.GetEncrypted(),
Type: w.GetType(),
Permissions: w.GetPermissions(),
ModifiedNs: w.GetModifiedNs(),
RawBlockSize: w.GetBlockSize(),
Platform: platformDataFromWire(w.GetPlatform()),
Deleted: w.GetDeleted(),
LocalFlags: localFlags,
NoPermissions: w.GetNoPermissions(),
Name: w.GetName(),
Size: w.GetSize(),
ModifiedS: w.GetModifiedS(),
ModifiedBy: ShortID(w.GetModifiedBy()),
Version: VectorFromWire(w.GetVersion()),
Sequence: w.GetSequence(),
Blocks: blocks,
SymlinkTarget: w.GetSymlinkTarget(),
BlocksHash: w.GetBlocksHash(),
PreviousBlocksHash: w.GetPreviousBlocksHash(),
Encrypted: w.GetEncrypted(),
Type: w.GetType(),
Permissions: w.GetPermissions(),
ModifiedNs: w.GetModifiedNs(),
RawBlockSize: w.GetBlockSize(),
Platform: platformDataFromWire(w.GetPlatform()),
Deleted: w.GetDeleted(),
LocalFlags: localFlags,
NoPermissions: w.GetNoPermissions(),
}
}
func FileInfoFromDB(w *bep.FileInfo) FileInfo {
f := FileInfoFromWire(w)
f.LocalFlags = FlagLocal(w.LocalFlags)
f.VersionHash = w.VersionHash
f.InodeChangeNs = w.InodeChangeNs
f.EncryptionTrailerSize = int(w.EncryptionTrailerSize)
return f
@@ -313,7 +330,6 @@ func FileInfoFromDB(w *bep.FileInfo) FileInfo {
func FileInfoFromDBTruncated(w FileInfoWithoutBlocks) FileInfo {
f := fileInfoFromWireWithBlocks(w, nil)
f.LocalFlags = FlagLocal(w.GetLocalFlags())
f.VersionHash = w.GetVersionHash()
f.InodeChangeNs = w.GetInodeChangeNs()
f.EncryptionTrailerSize = int(w.GetEncryptionTrailerSize())
f.truncated = true
@@ -323,14 +339,14 @@ func FileInfoFromDBTruncated(w FileInfoWithoutBlocks) FileInfo {
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, VersionHash:%x, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.VersionHash, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.Platform, f.InodeChangeTime())
return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.Platform, f.InodeChangeTime())
case FileInfoTypeFile:
return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, VersionHash:%x, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, NumBlocks:%d, BlocksHash:%x, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.VersionHash, f.Size, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.RawBlockSize, len(f.Blocks), f.BlocksHash, f.Platform, f.InodeChangeTime())
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, NumBlocks:%d, BlocksHash:%x, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.RawBlockSize, len(f.Blocks), f.BlocksHash, f.Platform, f.InodeChangeTime())
case FileInfoTypeSymlink, FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile:
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, VersionHash:%x, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Type, f.Sequence, f.Version, f.VersionHash, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.SymlinkTarget, f.Platform, f.InodeChangeTime())
return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q, Platform:%v, InodeChangeTime:%v}",
f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.IsInvalid(), f.LocalFlags, f.NoPermissions, f.SymlinkTarget, f.Platform, f.InodeChangeTime())
default:
panic("mystery file type detected")
}

View File

@@ -31,6 +31,8 @@ var bufPool = sync.Pool{
},
}
const hashLength = sha256.Size
var hashPool = sync.Pool{
New: func() any {
return sha256.New()
@@ -43,9 +45,6 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
counter = &noopCounter{}
}
hf := hashPool.Get().(hash.Hash) //nolint:forcetypeassert
const hashLength = sha256.Size
var blocks []protocol.BlockInfo
var hashes, thisHash []byte
@@ -62,8 +61,14 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
hashes = make([]byte, 0, hashLength*numBlocks)
}
hf := hashPool.Get().(hash.Hash) //nolint:forcetypeassert
// A 32k buffer is used for copying into the hash function.
buf := bufPool.Get().(*[bufSize]byte)[:] //nolint:forcetypeassert
defer func() {
bufPool.Put((*[bufSize]byte)(buf))
hf.Reset()
hashPool.Put(hf)
}()
var offset int64
lr := io.LimitReader(r, int64(blocksize)).(*io.LimitedReader)
@@ -102,9 +107,6 @@ func Blocks(ctx context.Context, r io.Reader, blocksize int, sizehint int64, cou
hf.Reset()
}
bufPool.Put((*[bufSize]byte)(buf))
hf.Reset()
hashPool.Put(hf)
if len(blocks) == 0 {
// Empty file

View File

@@ -655,6 +655,7 @@ func (w *walker) updateFileInfo(dst, src protocol.FileInfo) protocol.FileInfo {
dst.Version = src.Version.Update(w.ShortID)
dst.ModifiedBy = w.ShortID
dst.LocalFlags = w.LocalFlags
dst.PreviousBlocksHash = src.BlocksHash
// Copy OS data from src to dst, unless it was already set on dst.
dst.Platform.MergeWith(&src.Platform)

View File

@@ -162,8 +162,12 @@ func (a *App) startup() error {
}()
}
perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond)
slog.Info("Measured hashing performance", "perf", fmt.Sprintf("%.02f MB/s", perf))
if slog.Default().Enabled(context.Background(), slog.LevelInfo) {
go func() {
perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond)
slog.Info("Measured hashing performance", "perf", fmt.Sprintf("%.02f MB/s", perf))
}()
}
if a.opts.ResetDeltaIdxs {
slog.Info("Reinitializing delta index IDs")

View File

@@ -52,7 +52,7 @@ type Report struct {
ExternalVersioning int `json:"externalVersioning,omitempty" metric:"folder_feature{feature=VersioningExternal},summary" since:"2"`
StaggeredVersioning int `json:"staggeredVersioning,omitempty" metric:"folder_feature{feature=VersioningStaggered},summary" since:"2"`
TrashcanVersioning int `json:"trashcanVersioning,omitempty" metric:"folder_feature{feature=VersioningTrashcan},summary" since:"2"`
} `json:"folderUses,omitempty" since:"2"`
} `json:"folderUses,omitzero" since:"2"`
DeviceUses struct {
Introducer int `json:"introducer,omitempty" metric:"device_feature{feature=Introducer},summary" since:"2"`
@@ -62,20 +62,20 @@ type Report struct {
CompressNever int `json:"compressNever,omitempty" metric:"device_feature{feature=CompressNever},summary" since:"2"`
DynamicAddr int `json:"dynamicAddr,omitempty" metric:"device_feature{feature=AddressDynamic},summary" since:"2"`
StaticAddr int `json:"staticAddr,omitempty" metric:"device_feature{feature=AddressStatic},summary" since:"2"`
} `json:"deviceUses,omitempty" since:"2"`
} `json:"deviceUses,omitzero" since:"2"`
Announce struct {
GlobalEnabled bool `json:"globalEnabled,omitempty" metric:"discovery_feature_count{feature=GlobalEnabled},gauge" since:"2"`
LocalEnabled bool `json:"localEnabled,omitempty" metric:"discovery_feature_count{feature=LocalEnabled},gauge" since:"2"`
DefaultServersDNS int `json:"defaultServersDNS,omitempty" metric:"discovery_default_servers,summary" since:"2"`
OtherServers int `json:"otherServers,omitempty" metric:"discovery_other_servers,summary" since:"2"`
} `json:"announce,omitempty" since:"2"`
} `json:"announce,omitzero" since:"2"`
Relays struct {
Enabled bool `json:"enabled,omitempty" metric:"relay_feature_enabled,gauge" since:"2"`
DefaultServers int `json:"defaultServers,omitempty" metric:"relay_feature_count{feature=DefaultServers},summary" since:"2"`
OtherServers int `json:"otherServers,omitempty" metric:"relay_feature_count{feature=OtherServers},summary" since:"2"`
} `json:"relays,omitempty" since:"2"`
} `json:"relays,omitzero" since:"2"`
UsesRateLimit bool `json:"usesRateLimit,omitempty" metric:"feature_count{feature=RateLimitsEnabled},gauge" since:"2"`
UpgradeAllowedManual bool `json:"upgradeAllowedManual,omitempty" metric:"feature_count{feature=UpgradeAllowedManual},gauge" since:"2"`
@@ -127,13 +127,13 @@ type Report struct {
SyncXattrs int `json:"syncXattrs,omitempty" metric:"folder_feature{feature=SyncXattrs},summary" since:"3"`
SendOwnership int `json:"sendOwnership,omitempty" metric:"folder_feature{feature=SendOwnership},summary" since:"3"`
SyncOwnership int `json:"syncOwnership,omitempty" metric:"folder_feature{feature=SyncOwnership},summary" since:"3"`
} `json:"folderUsesV3,omitempty" since:"3"`
} `json:"folderUsesV3,omitzero" since:"3"`
DeviceUsesV3 struct {
Untrusted int `json:"untrusted,omitempty" metric:"device_feature{feature=Untrusted},summary" since:"3"`
UsesRateLimit int `json:"usesRateLimit,omitempty" metric:"device_feature{feature=RateLimitsEnabled},summary" since:"3"`
MultipleConnections int `json:"multipleConnections,omitempty" metric:"device_feature{feature=MultipleConnections},summary" since:"3"`
} `json:"deviceUsesV3,omitempty" since:"3"`
} `json:"deviceUsesV3,omitzero" since:"3"`
GUIStats struct {
Enabled int `json:"enabled,omitempty" metric:"gui_feature_count{feature=Enabled},summary" since:"3"`
@@ -146,7 +146,7 @@ type Report struct {
ListenLocal int `json:"listenLocal,omitempty" metric:"gui_feature_count{feature=ListenLocal},summary" since:"3"`
ListenUnspecified int `json:"listenUnspecified,omitempty" metric:"gui_feature_count{feature=ListenUnspecified},summary" since:"3"`
Theme map[string]int `json:"theme,omitempty" metric:"gui_theme,summaryVec:theme" since:"3"`
} `json:"guiStats,omitempty" since:"3"`
} `json:"guiStats,omitzero" since:"3"`
BlockStats struct {
Total int `json:"total,omitempty" metric:"blocks_processed_total,gauge" since:"3"`
@@ -155,7 +155,7 @@ type Report struct {
Pulled int `json:"pulled,omitempty" metric:"blocks_processed{source=pulled},gauge" since:"3"`
CopyOrigin int `json:"copyOrigin,omitempty" metric:"blocks_processed{source=copy_origin},gauge" since:"3"`
CopyElsewhere int `json:"copyElsewhere,omitempty" metric:"blocks_processed{source=copy_elsewhere},gauge" since:"3"`
} `json:"blockStats,omitempty" since:"3"`
} `json:"blockStats,omitzero" since:"3"`
TransportStats map[string]int `json:"transportStats,omitempty" since:"3"`
@@ -169,32 +169,32 @@ type Report struct {
EscapedIncludes int `json:"escapedIncludes,omitempty" metric:"folder_ignore_lines{kind=escapedIncludes},summary" since:"3"`
DoubleStars int `json:"doubleStars,omitempty" metric:"folder_ignore_lines{kind=doubleStars},summary" since:"3"`
Stars int `json:"stars,omitempty" metric:"folder_ignore_lines{kind=stars},summary" since:"3"`
} `json:"ignoreStats,omitempty" since:"3"`
} `json:"ignoreStats,omitzero" since:"3"`
// Added in post processing
Received time.Time `json:"received,omitempty"`
Received time.Time `json:"received,omitzero"`
Date string `json:"date,omitempty"`
Address string `json:"address,omitempty"`
OS string `json:"os" metric:"reports_total,gaugeVec:os"`
Arch string `json:"arch" metric:"reports_total,gaugeVec:arch"`
Compiler string `json:"compiler" metric:"builder,gaugeVec:compiler"`
Builder string `json:"builder" metric:"builder,gaugeVec:builder"`
Distribution string `json:"distribution" metric:"builder,gaugeVec:distribution"`
Country string `json:"country" metric:"location,gaugeVec:country"`
CountryCode string `json:"countryCode" metric:"location,gaugeVec:countryCode"`
MajorVersion string `json:"majorVersion" metric:"reports_by_major_total,gaugeVec:version"`
OS string `json:"os,omitempty" metric:"reports_total,gaugeVec:os"`
Arch string `json:"arch,omitempty" metric:"reports_total,gaugeVec:arch"`
Compiler string `json:"compiler,omitempty" metric:"builder,gaugeVec:compiler"`
Builder string `json:"builder,omitempty" metric:"builder,gaugeVec:builder"`
Distribution string `json:"distribution,omitempty" metric:"builder,gaugeVec:distribution"`
Country string `json:"country,omitempty" metric:"location,gaugeVec:country"`
CountryCode string `json:"countryCode,omitempty" metric:"location,gaugeVec:countryCode"`
MajorVersion string `json:"majorVersion,omitempty" metric:"reports_by_major_total,gaugeVec:version"`
// Once more to create a metric on OS, arch, distribution
DistDist string `json:"distDist" metric:"distribution,gaugeVec:distribution"`
DistOS string `json:"distOS" metric:"distribution,gaugeVec:os"`
DistArch string `json:"distArch" metric:"distribution,gaugeVec:arch"`
DistDist string `json:"distDist,omitempty" metric:"distribution,gaugeVec:distribution"`
DistOS string `json:"distOS,omitempty" metric:"distribution,gaugeVec:os"`
DistArch string `json:"distArch,omitempty" metric:"distribution,gaugeVec:arch"`
// Database counts
Database struct {
ModernCSQLite bool `json:"modernCSQLite" metric:"database_engine{engine=modernc-sqlite},gauge"`
MattnSQLite bool `json:"mattnSQLite" metric:"database_engine{engine=mattn-sqlite},gauge"`
LevelDB bool `json:"levelDB" metric:"database_engine{engine=leveldb},gauge"`
} `json:"database"`
ModernCSQLite bool `json:"modernCSQLite,omitempty" metric:"database_engine{engine=modernc-sqlite},gauge"`
MattnSQLite bool `json:"mattnSQLite,omitempty" metric:"database_engine{engine=mattn-sqlite},gauge"`
LevelDB bool `json:"levelDB,omitempty" metric:"database_engine{engine=leveldb},gauge"`
} `json:"database,omitzero"`
}
func New() *Report {

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STDISCOSRV" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "STDISCOSRV" "1" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STRELAYSRV" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "STRELAYSRV" "1" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-BEP" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-BEP" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-CONFIG" "5" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH OVERVIEW
@@ -148,7 +148,7 @@ may no longer correspond to the defaults.
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<maxConcurrentWrites>16</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
@@ -250,7 +250,7 @@ may no longer correspond to the defaults.
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<maxConcurrentWrites>16</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
@@ -340,7 +340,7 @@ GUI.
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<maxConcurrentWrites>16</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
@@ -607,7 +607,7 @@ folder is located on a FAT partition, and \fB0\fP otherwise.
.TP
.B maxConcurrentWrites
Maximum number of concurrent write operations while syncing. Increasing this might increase or
decrease disk performance, depending on the underlying storage. Default is \fB2\fP\&.
decrease disk performance, depending on the underlying storage. Default is \fB16\fP\&.
.UNINDENT
.INDENT 0.0
.TP
@@ -1555,7 +1555,7 @@ are set, \fI\%\-\-auditfile\fP takes priority.
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<maxConcurrentWrites>16</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-DEVICE-IDS" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-EVENT-API" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-FAQ" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-GLOBALDISCO" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-LOCALDISCO" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-NETWORKING" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-RELAY" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?
@@ -237,9 +237,10 @@ ResponseNotFound \- Session key is invalid
ResponseAlreadyConnected \- Session is full (both sides already connected)
.IP 3. 3
ResponseSuccess \- You have successfully joined the session
.IP 4. 3
RelayFull \- Relay limits are too strict for you to be able to join the session.
.UNINDENT
.sp
4. RelayFull \- Relay limits are too strict for you to be able to join the session.
The relay immediately terminates the connection after sending this.
.sp
After the successful response, all the bytes written and received will be

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-REST-API" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp
@@ -156,7 +156,7 @@ Returns the current configuration.
\(dqmarkerName\(dq: \(dq.stfolder\(dq,
\(dqcopyOwnershipFromParent\(dq: false,
\(dqmodTimeWindowS\(dq: 0,
\(dqmaxConcurrentWrites\(dq: 2,
\(dqmaxConcurrentWrites\(dq: 16,
\(dqdisableFsync\(dq: false,
\(dqblockPullOrder\(dq: \(dqstandard\(dq,
\(dqcopyRangeMethod\(dq: \(dqstandard\(dq,
@@ -328,7 +328,7 @@ Returns the current configuration.
\(dqmarkerName\(dq: \(dq.stfolder\(dq,
\(dqcopyOwnershipFromParent\(dq: false,
\(dqmodTimeWindowS\(dq: 0,
\(dqmaxConcurrentWrites\(dq: 2,
\(dqmaxConcurrentWrites\(dq: 16,
\(dqdisableFsync\(dq: false,
\(dqblockPullOrder\(dq: \(dqstandard\(dq,
\(dqcopyRangeMethod\(dq: \(dqstandard\(dq,
@@ -398,14 +398,14 @@ config, modify the needed parts and post it again.
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Return format changed in versions 0.13.0, 1.19.0 and 1.23.0.
Return format changed in versions 0.13.0, 0.14.14, 1.2.0, 1.19.0, 1.23.0 and 1.25.0.
.UNINDENT
.UNINDENT
.sp
Returns the list of configured devices and some metadata associated
with them. The list also contains the local device itself as not connected.
with them.
.sp
The connection types are \fBTCP (Client)\fP, \fBTCP (Server)\fP, \fBRelay (Client)\fP and \fBRelay (Server)\fP\&.
The connection types are \fBtcp\-client\fP, \fBtcp\-server\fP, \fBrelay\-client\fP, \fBrelay\-server\fP, \fBquic\-client\fP and \fBquic\-server\fP\&.
.INDENT 0.0
.INDENT 3.5
.sp
@@ -446,7 +446,7 @@ The connection types are \fBTCP (Client)\fP, \fBTCP (Server)\fP, \fBRelay (Clien
\(dqoutBytesTotal\(dq: 550,
\(dqpaused\(dq: false,
\(dqstartedAt\(dq: \(dq2015\-11\-07T00:09:47Z\(dq,
\(dqtype\(dq: \(dqTCP (Client)\(dq
\(dqtype\(dq: \(dqtcp\-client\(dq
}
},
\(dqtotal\(dq: {

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-SECURITY" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-STIGNORE" "5" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-VERSIONING" "7" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING" "1" "Aug 29, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING" "1" "Sep 14, 2025" "v2.0.0" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS
@@ -43,10 +43,12 @@ syncthing [serve]
[\-\-gui\-address=<address>] [\-\-gui\-apikey=<key>]
[\-\-log\-level=<level>] [\-\-log\-file=<filename>]
[\-\-log\-max\-old\-files=<num>] [\-\-log\-max\-size=<num>]
[\-\-log\-format\-timestamp=<format>] [\-\-no\-log\-format\-level\-string]
[\-\-log\-format\-level\-syslog]
[\-\-no\-browser] [\-\-no\-console]
[\-\-no\-port\-probing] [\-\-no\-restart] [\-\-no\-upgrade]
[\-\-paused] [\-\-unpaused]
[\-\-verbose] [\-\-version] [\-\-help] [\-\-debug\-*]
[\-\-version] [\-\-help] [\-\-debug\-*]
syncthing cli
[\-\-home=<dir> | \-\-config=<dir> \-\-data=<dir>]
@@ -112,7 +114,7 @@ few log messages.
.SH COMMON OPTIONS
.INDENT 0.0
.TP
.B \-\-home=<dir>, \-H <dir>
.B \-\-home=<dir>, \-H <dir> ($STHOMEDIR)
Set common configuration and data directory. The default configuration
directory is \fB$XDG_STATE_HOME/syncthing\fP or
\fB$HOME/.local/state/syncthing\fP (Unix\-like),
@@ -121,13 +123,13 @@ directory is \fB$XDG_STATE_HOME/syncthing\fP or
.UNINDENT
.INDENT 0.0
.TP
.B \-\-config=<dir>, \-C <dir>
.B \-\-config=<dir>, \-C <dir> ($STCONFDIR)
Set configuration directory. Alternative to \fB\-\-home\fP and must be used
together with \fB\-\-data\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-data=<dir>, \-D <dir>
.B \-\-data=<dir>, \-D <dir> ($STDATADIR)
Set data (e.g. database) directory. Alternative to \fB\-\-home\fP and must be used
together with \fB\-\-config\fP\&.
.UNINDENT
@@ -140,36 +142,38 @@ given subcommand.
.SH SERVE OPTIONS
.INDENT 0.0
.TP
.B \-\-allow\-newer\-config
.B \-\-allow\-newer\-config ($STALLOWNEWERCONFIG)
Try loading a config file written by a newer program version, instead of
failing immediately.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-audit
.B \-\-audit ($STAUDIT)
Write events to timestamped file \fBaudit\-YYYYMMDD\-HHMMSS.log\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-auditfile=<file|\-|\-\->
.B \-\-auditfile=<file|\-|\-\-> ($STAUDITFILE)
Use specified file or stream (\fB\(dq\-\(dq\fP for stdout, \fB\(dq\-\-\(dq\fP for stderr) for
audit events, rather than the timestamped default file name.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-db\-maintenance\-interval=<interval>
.B \-\-db\-maintenance\-interval=<interval> ($STDBMAINTENANCEINTERVAL)
Database maintenance interval internal database maintenance routines
run this often.
run this often. The format is that of a Go duration string (see
\X'tty: link https://pkg.go.dev/time#ParseDuration'\fI\%https://pkg.go.dev/time#ParseDuration\fP\X'tty: link').
.UNINDENT
.INDENT 0.0
.TP
.B \-\-db\-delete\-retention\-interval=<interval>
.B \-\-db\-delete\-retention\-interval=<interval> ($STDBDELETERETENTIONINTERVAL)
Database deleted item retention interval deleted items are forgotten
from the database after this interval.
from the database after this interval. The format is that of a Go duration
string (see \X'tty: link https://pkg.go.dev/time#ParseDuration'\fI\%https://pkg.go.dev/time#ParseDuration\fP\X'tty: link').
.UNINDENT
.INDENT 0.0
.TP
.B \-\-gui\-address=<address>
.B \-\-gui\-address=<address> ($STGUIADDRESS)
Override GUI listen address. Set this to an address (\fB0.0.0.0:8384\fP)
or a URL (\fBhttp://0.0.0.0:8384\fP). Supported schemes are \fBhttp\fP for
plain HTTP, \fBhttps\fP for HTTP over TLS, \fBunix\fP for plain Unix sockets
@@ -179,79 +183,95 @@ as part of the URL structure, one to specify an absolute path).
.UNINDENT
.INDENT 0.0
.TP
.B \-\-gui\-apikey=<string>
.B \-\-gui\-apikey=<string> ($STGUIAPIKEY)
Override the API key needed to access the GUI / REST API.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-log\-level=<level>
Set the log level for all packages. Valid levels are DEBUG, INFO, WARN,
and ERROR.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-log\-file=<filename>
.B \-\-log\-file=<filename> ($STLOGFILE)
Set destination filename for logging (use \fB\(dq\-\(dq\fP for stdout, which is the
default option).
.UNINDENT
.INDENT 0.0
.TP
.B \-\-log\-max\-old\-files=<num>
.B \-\-log\-level=<level> ($STLOGLEVEL)
Set the log level for all packages. Valid levels are DEBUG, INFO, WARN,
and ERROR.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-log\-max\-old\-files=<num> ($STLOGMAXOLDFILES)
Number of old files to keep (zero to keep only current). Applies only when
log rotation is enabled through \fB\-\-log\-max\-size\fP\&.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-log\-max\-size=<num>
Maximum size of any log file (zero to disable log rotation).
.B \-\-log\-max\-size=<num> ($STLOGMAXSIZE)
Maximum size in bytes of any log file (zero to disable log rotation).
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-browser
.B \-\-log\-format\-timestamp=<format> ($STLOGFORMATTIMESTAMP)
Format for timestamp, set to empty to disable timestamps. The format is that
of the Go time formatter (see \X'tty: link https://pkg.go.dev/time#Layout'\fI\%https://pkg.go.dev/time#Layout\fP\X'tty: link').
.UNINDENT
.INDENT 0.0
.TP
.B \-\-[no\-]log\-format\-level\-string ($STLOGFORMATLEVELSTRING)
Whether to include level string (e.g. “INF”) in log line.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-[no\-]log\-format\-level\-syslog ($STLOGFORMATLEVELSYSLOG)
Whether to include level as syslog prefix (e.g. “<6>”) in log line.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-browser ($STNOBROWSER)
Do not start a browser.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-console
.B \-\-no\-console ($STNOCONSOLE)
Hide the console window. (On Windows only)
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-port\-probing
.B \-\-no\-port\-probing ($STNOPORTPROBING)
Dont try to find unused random ports for the GUI and listen address when
generating an initial configuration / starting for the first time.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-restart
.B \-\-no\-restart ($STNORESTART)
Do not restart Syncthing when it exits. The monitor process will still run
to handle crashes and writing to logfiles (if configured to).
.UNINDENT
.INDENT 0.0
.TP
.B \-\-no\-upgrade
Disable automatic upgrades. Equivalent to the \fBSTNOUPGRADE\fP environment
variable, see below.
.B \-\-no\-upgrade ($STNOUPGRADE)
Disable automatic upgrades.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-paused
.B \-\-paused ($STPAUSED)
Start with all devices and folders paused.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-unpaused
.B \-\-unpaused ($STUNPAUSED)
Start with all devices and folders unpaused.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-upgrade
Perform upgrade.
.B \-\-version
Show the current version information, then exit.
.UNINDENT
.INDENT 0.0
.TP
.B \-\-verbose
Print verbose log output.
.B \-\-debug\-*
Several debug options exist. See \fBsyncthing serve \-\-help\fP for the flags
that apply to your version.
.UNINDENT
.SH DECRYPT OPTIONS
.INDENT 0.0
@@ -266,7 +286,7 @@ Dont write decrypted files to disk (but verify plaintext hashes).
.UNINDENT
.INDENT 0.0
.TP
.B \-\-password=<pw>
.B \-\-password=<pw> ($FOLDER_PASSWORD)
Folder password for decryption / verification. Can be passed through the
\fBFOLDER_PASSWORD\fP environment variable instead to avoid recording in a
shells history buffer or sniffing from the running processes list.
@@ -337,9 +357,9 @@ signal handler. Exit codes over 128+N on Unix usually represent the signal which
caused the process to exit. For example, \fB128 + 9 (SIGKILL) = 137\fP\&.
.SH SUBCOMMANDS
.sp
The command line syntax actually supports different modes of operation through
several subcommands, specified as the first argument. If omitted, the default
\fBserve\fP is assumed.
The command line syntax supports different modes of operation through
several subcommands, specified as the first argument. If omitted, the
default \fBserve\fP is assumed.
.sp
The initial setup of a device ID and default configuration can be called
explicitly with the \fBgenerate\fP subcommand. It can also update the configured
@@ -423,38 +443,79 @@ facilities to trace: \fBapi,beacon\fP\&. Optionally, a log level can be
given per facility to specify something other than DEBUG:
\fBapi:WARN,beacon:ERR\fP, potentially overriding a global \fB\-\-log\-level\fP
adjustment.
.INDENT 7.0
.INDENT 3.5
.sp
The valid facility strings are listed below; additionally, \fBsyncthing
serve \-\-help\fP always outputs the most up\-to\-date list.
.INDENT 0.0
.INDENT 3.5
api \- REST API
beacon \- Multicast and broadcast discovery
config \- Configuration loading and saving
connections \- Connection handling
db/sqlite \- SQLite database
dialer \- Dialing connections
discover \- Remote device discovery
events \- Event generation and logging
fs \- Filesystem access
main \- Main package
model \- The root hub
nat \- NAT discovery and port mapping
pmp \- NAT\-PMP discovery and port mapping
protocol \- The BEP protocol
relay/client \- Relay client
scanner \- File change detection and hashing
stun \- STUN functionality
syncthing \- Main run facility
upgrade \- Binary upgrades
upnp \- UPnP discovery and port mapping
ur \- Usage reporting
versioner \- File versioning
watchaggregator \- Filesystem event watcher
.UNINDENT
.UNINDENT
.UNINDENT
.INDENT 7.0
.TP
.B api
REST API
.TP
.B beacon
Multicast and broadcast discovery
.TP
.B config
Configuration loading and saving
.TP
.B connections
Connection handling
.TP
.B db/sqlite
SQLite database
.TP
.B dialer
Dialing connections
.TP
.B discover
Remote device discovery
.TP
.B events
Event generation and logging
.TP
.B fs
Filesystem access
.TP
.B main
Main package
.TP
.B model
The root hub
.TP
.B nat
NAT discovery and port mapping
.TP
.B pmp
NAT\-PMP discovery and port mapping
.TP
.B protocol
The BEP protocol
.TP
.B relay/client
Relay client
.TP
.B scanner
File change detection and hashing
.TP
.B stun
STUN functionality
.TP
.B syncthing
Main run facility
.TP
.B upgrade
Binary upgrades
.TP
.B upnp
UPnP discovery and port mapping
.TP
.B ur
Usage reporting
.TP
.B versioner
File versioning
.TP
.B watchaggregator
Filesystem event watcher
.UNINDENT
.TP
.B STLOCKTHRESHOLD

View File

@@ -113,6 +113,7 @@ message FileInfo {
repeated BlockInfo blocks = 16;
bytes symlink_target = 17;
bytes blocks_hash = 18;
bytes previous_blocks_hash = 20;
bytes encrypted = 19;
FileInfoType type = 2;
uint32 permissions = 4;
@@ -127,7 +128,7 @@ message FileInfo {
uint32 local_flags = 1000;
// The version_hash is an implementation detail and not part of the wire
// format.
// format. It is used in the old database format.
bytes version_hash = 1001;
// The time when the inode was last changed (i.e., permissions, xattrs

View File

@@ -17,6 +17,7 @@ message FileInfoTruncated {
reserved 16; // blocks
bytes symlink_target = 17;
bytes blocks_hash = 18;
bytes previous_blocks_hash = 20;
bytes encrypted = 19;
bep.FileInfoType type = 2;
uint32 permissions = 4;