Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4d08b336e | ||
|
|
97cb72a608 | ||
|
|
c9236b1adc | ||
|
|
5313c75eba | ||
|
|
40febaa811 | ||
|
|
92ff9ac078 | ||
|
|
f7f2e7c70b | ||
|
|
3d168d6e44 | ||
|
|
8cc4c4ffb3 |
@@ -192,7 +192,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "~1.25.7" # temporarily stay on Go 1.25 due to linker error on Go 1.26
|
||||
go-version: ${{ needs.facts.outputs.go-version }}
|
||||
cache: false
|
||||
|
||||
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
|
||||
|
||||
@@ -149,6 +149,7 @@ HansK-p <42314815+HansK-p@users.noreply.github.com>
|
||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||
Hazem Krimi <me@hazemkrimi.tech>
|
||||
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
||||
Henrik Bråthen <henrikbs@proton.me>
|
||||
Hireworks <129852174+hireworksltd@users.noreply.github.com>
|
||||
Hugo Locurcio <hugo.locurcio@hugo.pro>
|
||||
Iain Barnett <iainspeed@gmail.com>
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Negatiboa ez den zenbaki bat hauta ezazu (\"2.35\" adib.) bai eta unitate bat. Disko osoaren ehuneko espazioa",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Abantailatua ez den portu zenbalki bat sar ezazu (1024 - 65535)",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Sartu komaz bereizitako helbideak (\"tcp://ip:port\", \"tcp://host:port\"), edo \"dynamic\" helbidea automatikoki bilatzeko.",
|
||||
"Enter ignore patterns, one per line.": "Ezkluzio filtroak sar, lerro bakoitzean bat bakarrik.",
|
||||
"Enter ignore patterns, one per line.": "Sartu baztertze-patroiak, bakarra lerro bakoitzeko.",
|
||||
"Enter up to three octal digits.": "Sartu 3 digitu bitarte",
|
||||
"Error": "Hutsa",
|
||||
"External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa",
|
||||
@@ -229,7 +229,7 @@
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Partekatzearen izen hautuzkoa eta atsegina, zure gisa. Tresna bakotxean desberdina izaiten ahal da.",
|
||||
"Options": "Hautuzkoak",
|
||||
"Out of Sync": "Ez sinkronizatua",
|
||||
"Out of Sync Items": "Ez sinkronizatu elementuak",
|
||||
"Out of Sync Items": "Sinkronizatu gabeko elementuak",
|
||||
"Outgoing Rate Limit (KiB/s)": "Bidaltze emari gehienekoa (KiB/s)",
|
||||
"Override": "Gainidatzi",
|
||||
"Override Changes": "Aldaketak desegin",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<h4 class="text-center" translate>The Syncthing Authors</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Norcombe, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, cui, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Epifeny, epifeny, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, JRNitre, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Luiz Angelo Daros de Luca, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, mattn, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Maxwell G, Michael Jephcote, Michael Rienstra, Michael Wang 汪東陽, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Prathik P Kulkarni, pullmerge, Quentin Hibon, Rahmi Pruitt, RealCharlesChia, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Shablone, Shivam Kumar, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Tao, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Umer-Azaz, Unrud, Val Markovic, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, vvaswani, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
|
||||
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Norcombe, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, cui, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Epifeny, epifeny, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Henrik Bråthen, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, JRNitre, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Luiz Angelo Daros de Luca, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, mattn, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Maxwell G, Michael Jephcote, Michael Rienstra, Michael Wang 汪東陽, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Prathik P Kulkarni, pullmerge, Quentin Hibon, Rahmi Pruitt, RealCharlesChia, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Shablone, Shivam Kumar, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Tao, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Umer-Azaz, Unrud, Val Markovic, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, vvaswani, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1139,20 +1139,20 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.syncPercentage = function (folder) {
|
||||
if (typeof $scope.model[folder] === 'undefined') {
|
||||
var model = $scope.model[folder];
|
||||
if (typeof model === 'undefined') {
|
||||
return 100;
|
||||
}
|
||||
if ($scope.model[folder].needTotalItems === 0) {
|
||||
if (model.needTotalItems === 0) {
|
||||
return 100;
|
||||
}
|
||||
if (($scope.model[folder].needBytes == 0 && $scope.model[folder].needDeletes > 0) || $scope.model[folder].globalBytes == 0) {
|
||||
// We don't need any data, but we have deletes that we need
|
||||
// to do. Drop down the completion percentage to indicate
|
||||
// that we have stuff to do.
|
||||
// Do the same thing in case we only have zero byte files to sync.
|
||||
if (model.needBytes == 0 && model.needTotalItems > 0) {
|
||||
// We don't need any data, but we have deletes, directories,
|
||||
// symlinks or zero byte files that we need to do. Drop down the
|
||||
// completion percentage to indicate that we have stuff to do.
|
||||
return 95;
|
||||
}
|
||||
return progressIntegerPercentage($scope.model[folder].inSyncBytes, $scope.model[folder].globalBytes);
|
||||
return progressIntegerPercentage(model.inSyncBytes, model.globalBytes);
|
||||
};
|
||||
|
||||
$scope.scanPercentage = function (folder) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
const (
|
||||
folderID = "test"
|
||||
blockSize = 128 << 10
|
||||
dirSize = 128
|
||||
)
|
||||
|
||||
func TestBasics(t *testing.T) {
|
||||
@@ -72,11 +71,11 @@ func TestBasics(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const (
|
||||
localSize = (1+2+3)*blockSize + dirSize
|
||||
localSize = (1+2+3)*blockSize
|
||||
remoteSize = (3 + 4 + 5) * blockSize
|
||||
globalSize = (2+3+3+4+5)*blockSize + dirSize
|
||||
globalSize = (2+3+3+4+5)*blockSize
|
||||
needSizeLocal = remoteSize
|
||||
needSizeRemote = (2+3)*blockSize + dirSize
|
||||
needSizeRemote = (2+3)*blockSize
|
||||
)
|
||||
|
||||
t.Run("SchemaVersion", func(t *testing.T) {
|
||||
|
||||
@@ -111,10 +111,6 @@ func (s *folderDB) Update(device protocol.DeviceID, fs []protocol.FileInfo, opti
|
||||
f.BlocksHash = nil
|
||||
}
|
||||
|
||||
if f.Type == protocol.FileInfoTypeDirectory {
|
||||
f.Size = 128 // synthetic directory size
|
||||
}
|
||||
|
||||
// Insert the file.
|
||||
//
|
||||
// If it is a remote file, set remote_sequence otherwise leave it at
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Copyright (C) 2026 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/.
|
||||
|
||||
-- All non-file entries should have size zero.
|
||||
-- Directories were previously stored with a "synthetic" size of 128.
|
||||
UPDATE files
|
||||
SET size = 0
|
||||
WHERE type != 0
|
||||
;
|
||||
@@ -106,6 +106,10 @@ func (i indirectFI) FileInfo() (protocol.FileInfo, error) {
|
||||
fi.Blocks = bl.Blocks
|
||||
}
|
||||
fi.Name = osutil.NativeFilename(fi.Name)
|
||||
// Undo earlier behaviour of setting a synthetic size on dirs.
|
||||
if fi.Type != protocol.FileInfoTypeFile && fi.Size > 0 {
|
||||
fi.Size = 0
|
||||
}
|
||||
return protocol.FileInfoFromDB(&fi), nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1774,7 +1774,7 @@ func fileIntfJSONMap(f protocol.FileInfo) map[string]interface{} {
|
||||
out := map[string]interface{}{
|
||||
"name": f.FileName(),
|
||||
"type": f.FileType().String(),
|
||||
"size": f.FileSize(),
|
||||
"size": f.Size,
|
||||
"deleted": f.IsDeleted(),
|
||||
"invalid": f.IsInvalid(),
|
||||
"ignored": f.IsIgnored(),
|
||||
|
||||
+9
-3
@@ -1762,9 +1762,10 @@ func TestConfigChanges(t *testing.T) {
|
||||
|
||||
folder2Path := "/rest/config/folders/folder2"
|
||||
|
||||
// Create a folder and add another
|
||||
// Create a folder and add another. Give folder2 a non-default
|
||||
// RescanIntervalS so we can verify it survives a later partial PATCH.
|
||||
mod(http.MethodPut, "/rest/config/folders", []config.FolderConfiguration{{ID: "folder1", Path: "folder1"}})
|
||||
mod(http.MethodPut, folder2Path, config.FolderConfiguration{ID: "folder2", Path: "folder2"})
|
||||
mod(http.MethodPut, folder2Path, config.FolderConfiguration{ID: "folder2", Path: "folder2", RescanIntervalS: 1234})
|
||||
|
||||
// Check they are there
|
||||
get("/rest/config/folders/folder1").Body.Close()
|
||||
@@ -1779,9 +1780,14 @@ func TestConfigChanges(t *testing.T) {
|
||||
if err := unmarshalTo(resp.Body, &folder); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !dev.Paused {
|
||||
if !folder.Paused {
|
||||
t.Error("Expected folder to be paused")
|
||||
}
|
||||
// A partial PATCH must not reset other (default-tagged) attributes to
|
||||
// their default values.
|
||||
if folder.RescanIntervalS != 1234 {
|
||||
t.Error("Expected RescanIntervalS to be preserved as 1234, got", folder.RescanIntervalS)
|
||||
}
|
||||
|
||||
// Delete folder2
|
||||
req, _ := http.NewRequest(http.MethodDelete, baseURL+folder2Path, nil)
|
||||
|
||||
@@ -9,7 +9,6 @@ package config
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -395,16 +394,6 @@ func (f XattrFilter) GetMaxTotalSize() int {
|
||||
return f.MaxTotalSize
|
||||
}
|
||||
|
||||
func (f *FolderConfiguration) UnmarshalJSON(data []byte) error {
|
||||
structutil.SetDefaults(f)
|
||||
|
||||
// avoid recursing into this method
|
||||
type noCustomUnmarshal FolderConfiguration
|
||||
ptr := (*noCustomUnmarshal)(f)
|
||||
|
||||
return json.Unmarshal(data, ptr)
|
||||
}
|
||||
|
||||
func (f *FolderConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
structutil.SetDefaults(f)
|
||||
|
||||
|
||||
+6
-3
@@ -975,10 +975,13 @@ func (f *folder) scanTimerFired(ctx context.Context) error {
|
||||
select {
|
||||
case <-f.initialScanFinished:
|
||||
default:
|
||||
if err != nil {
|
||||
f.sl.ErrorContext(ctx, "Failed initial scan", slogutil.Error(err))
|
||||
} else {
|
||||
switch {
|
||||
case err == nil:
|
||||
f.sl.InfoContext(ctx, "Completed initial scan")
|
||||
case errors.Is(err, context.Canceled):
|
||||
// Suppressed: context was canceled (e.g. by user)
|
||||
default:
|
||||
f.sl.ErrorContext(ctx, "Failed initial scan", slogutil.Error(err))
|
||||
}
|
||||
close(f.initialScanFinished)
|
||||
}
|
||||
|
||||
@@ -163,12 +163,11 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
// We now have a newer file than the rest of the cluster. Global state should reflect this.
|
||||
|
||||
size = mustV(m.GlobalSize("ro"))
|
||||
const sizeOfDir = 128
|
||||
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
|
||||
if size.Files != 1 || size.Bytes != int64(len(oldData)) {
|
||||
t.Fatalf("Global: expected no change due to the new file: %+v", size)
|
||||
}
|
||||
size = mustV(m.LocalSize("ro", protocol.LocalDeviceID))
|
||||
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
|
||||
if size.Files != 1 || size.Bytes != int64(len(newData)) {
|
||||
t.Fatalf("Local: expected the new file to be reflected: %+v", size)
|
||||
}
|
||||
size = mustV(m.NeedSize("ro", protocol.LocalDeviceID))
|
||||
@@ -185,11 +184,11 @@ func TestRecvOnlyRevertNeeds(t *testing.T) {
|
||||
m.Revert("ro")
|
||||
|
||||
size = mustV(m.GlobalSize("ro"))
|
||||
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
|
||||
if size.Files != 1 || size.Bytes != int64(len(oldData)) {
|
||||
t.Fatalf("Global: expected the global size to revert: %+v", size)
|
||||
}
|
||||
size = mustV(m.LocalSize("ro", protocol.LocalDeviceID))
|
||||
if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
|
||||
if size.Files != 1 || size.Bytes != int64(len(newData)) {
|
||||
t.Fatalf("Local: expected the local size to remain: %+v", size)
|
||||
}
|
||||
size = mustV(m.NeedSize("ro", protocol.LocalDeviceID))
|
||||
|
||||
+8
-21
@@ -2548,13 +2548,6 @@ func (m *model) DelayScan(folder string, next time.Duration) {
|
||||
func (m *model) numHashers(folder string) int {
|
||||
m.mut.RLock()
|
||||
folderCfg := m.folderCfgs[folder]
|
||||
numFolders := max(1, len(m.folderCfgs))
|
||||
// MaxFolderConcurrency already limits the number of scanned folders, so
|
||||
// prefer it over the overall number of folders to avoid limiting performance
|
||||
// further for no reason.
|
||||
if concurrency := m.cfg.Options().MaxFolderConcurrency(); concurrency > 0 {
|
||||
numFolders = min(numFolders, concurrency)
|
||||
}
|
||||
m.mut.RUnlock()
|
||||
|
||||
if folderCfg.Hashers > 0 {
|
||||
@@ -2562,21 +2555,15 @@ func (m *model) numHashers(folder string) int {
|
||||
return folderCfg.Hashers
|
||||
}
|
||||
|
||||
numCpus := runtime.GOMAXPROCS(-1)
|
||||
if build.IsWindows || build.IsDarwin || build.IsIOS || build.IsAndroid {
|
||||
// Interactive operating systems; don't load the system too heavily by
|
||||
// default.
|
||||
numCpus = max(1, numCpus/4)
|
||||
numCPUs := runtime.GOMAXPROCS(-1)
|
||||
switch {
|
||||
case build.IsWindows || build.IsIOS || build.IsAndroid:
|
||||
// Use a quarter of the CPU cores on interactive or constrained OSes
|
||||
return max(1, numCPUs/4)
|
||||
default:
|
||||
// Otherwise use up to half
|
||||
return max(1, numCPUs/2)
|
||||
}
|
||||
|
||||
// For other operating systems and architectures, lets try to get some
|
||||
// work done... Divide the available CPU cores among the configured
|
||||
// folders.
|
||||
if perFolder := numCpus / numFolders; perFolder > 0 {
|
||||
return perFolder
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// generateClusterConfig returns a ClusterConfigMessage that is correct and the
|
||||
|
||||
@@ -1787,10 +1787,12 @@ func TestGlobalDirectoryTree(t *testing.T) {
|
||||
b := func(isfile bool, path ...string) protocol.FileInfo {
|
||||
typ := protocol.FileInfoTypeDirectory
|
||||
var blocks []protocol.BlockInfo
|
||||
var size int64
|
||||
|
||||
if isfile {
|
||||
typ = protocol.FileInfoTypeFile
|
||||
blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
|
||||
size = 0xa
|
||||
}
|
||||
seq++
|
||||
return protocol.FileInfo{
|
||||
@@ -1798,7 +1800,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
|
||||
Type: typ,
|
||||
ModifiedS: 0x666,
|
||||
Blocks: blocks,
|
||||
Size: 0xa,
|
||||
Size: size,
|
||||
Sequence: seq,
|
||||
}
|
||||
}
|
||||
@@ -1814,7 +1816,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
|
||||
return &TreeEntry{
|
||||
Name: name,
|
||||
ModTime: time.Unix(0x666, 0),
|
||||
Size: 128,
|
||||
Size: 0,
|
||||
Type: protocol.FileInfoTypeDirectory.String(),
|
||||
Children: entries,
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ package osutil_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
@@ -59,11 +61,16 @@ func TestTraversesSymlink(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue4875(t *testing.T) {
|
||||
testFsPath := rand.String(32)
|
||||
testFs := fs.NewFilesystem(fs.FilesystemTypeFake, testFsPath)
|
||||
func TestSymlinkedFSRootTraversal(t *testing.T) {
|
||||
// Normal FS operations should work inside a folder root that is a symlink
|
||||
if build.IsWindows {
|
||||
t.Skip("symlinks unavailable")
|
||||
}
|
||||
|
||||
testFsPath := t.TempDir()
|
||||
testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, testFsPath)
|
||||
testFs.MkdirAll(filepath.Join("a", "b", "c"), 0o755)
|
||||
if err := testFs.CreateSymlink(filepath.Join("a", "b"), filepath.Join("a", "l")); err != nil {
|
||||
if err := testFs.CreateSymlink("b", filepath.Join("a", "l")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -76,10 +83,21 @@ func TestIssue4875(t *testing.T) {
|
||||
t.Fatal("error in setup, a/l/c should be a directory")
|
||||
}
|
||||
|
||||
testFs = fs.NewFilesystem(fs.FilesystemTypeFake, filepath.Join(testFsPath, "a/l"))
|
||||
testFs = fs.NewFilesystem(fs.FilesystemTypeBasic, filepath.Join(testFsPath, "a/l"))
|
||||
if err := osutil.TraversesSymlink(testFs, "."); err != nil {
|
||||
t.Error(`TraversesSymlink on filesystem with symlink at root returned error for ".":`, err)
|
||||
}
|
||||
|
||||
// We should be able to create and list files within the symlinked root
|
||||
if _, err := testFs.Create("test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if names, err := testFs.DirNames("."); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !slices.Contains(names, "test") {
|
||||
t.Log(names)
|
||||
t.Error("missing test file in listing")
|
||||
}
|
||||
}
|
||||
|
||||
var traversesSymlinkResult error
|
||||
|
||||
@@ -389,16 +389,6 @@ func (f FileInfo) HasPermissionBits() bool {
|
||||
return !f.NoPermissions
|
||||
}
|
||||
|
||||
func (f FileInfo) FileSize() int64 {
|
||||
if f.Deleted {
|
||||
return 0
|
||||
}
|
||||
if f.IsDirectory() || f.IsSymlink() {
|
||||
return SyntheticDirectorySize
|
||||
}
|
||||
return f.Size
|
||||
}
|
||||
|
||||
func (f FileInfo) BlockSize() int {
|
||||
if f.RawBlockSize < MinBlockSize {
|
||||
return MinBlockSize
|
||||
|
||||
@@ -56,7 +56,8 @@ const (
|
||||
// DesiredPerFileBlocks is the number of blocks we aim for per file
|
||||
DesiredPerFileBlocks = 2000
|
||||
|
||||
SyntheticDirectorySize = 128
|
||||
// We used to send this size for directories and still accept that for compat.
|
||||
deprecatedSyntheticDirectorySize = 128
|
||||
|
||||
// don't bother compressing messages smaller than this many bytes
|
||||
compressionThreshold = 128
|
||||
@@ -640,7 +641,7 @@ func checkFileInfoConsistency(f FileInfo) error {
|
||||
// Only files should have blocks
|
||||
return errNonFileHasBlocks
|
||||
|
||||
case f.IsDirectory() && f.Size != 0 && f.Size != SyntheticDirectorySize:
|
||||
case f.IsDirectory() && f.Size != 0 && f.Size != deprecatedSyntheticDirectorySize:
|
||||
// Directories should be size zero or the synthetic directory size
|
||||
return errNonFileHasSize
|
||||
|
||||
|
||||
@@ -464,7 +464,7 @@ func TestCheckConsistency(t *testing.T) {
|
||||
fi: FileInfo{
|
||||
Name: "foo",
|
||||
Type: FileInfoTypeDirectory,
|
||||
Size: SyntheticDirectorySize,
|
||||
Size: deprecatedSyntheticDirectorySize,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -492,7 +492,7 @@ func TestCheckConsistency(t *testing.T) {
|
||||
Name: "foo",
|
||||
Type: FileInfoTypeSymlink,
|
||||
SymlinkTarget: []byte("bar"),
|
||||
Size: SyntheticDirectorySize,
|
||||
Size: deprecatedSyntheticDirectorySize,
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
|
||||
@@ -41,9 +41,9 @@ type testfileList []testfile
|
||||
|
||||
var testdata = testfileList{
|
||||
{"afile", 4, "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
|
||||
{"dir1", 128, ""},
|
||||
{"dir1", 0, ""},
|
||||
{filepath.Join("dir1", "dfile"), 5, "49ae93732fcf8d63fe1cce759664982dbd5b23161f007dba8561862adc96d063"},
|
||||
{"dir2", 128, ""},
|
||||
{"dir2", 0, ""},
|
||||
{filepath.Join("dir2", "cfile"), 4, "bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c"},
|
||||
{"excludes", 37, "df90b52f0c55dba7a7a940affe482571563b1ac57bd5be4d8a0291e7de928e06"},
|
||||
{"further-excludes", 5, "7eb0a548094fa6295f7fd9200d69973e5f5ec5c04f2a86d998080ac43ecf89f1"},
|
||||
@@ -601,7 +601,7 @@ func (l fileList) testfiles() testfileList {
|
||||
if len(f.Blocks) > 1 {
|
||||
panic("simple test case stuff only supports a single block per file")
|
||||
}
|
||||
testfiles[i] = testfile{name: f.Name, length: f.FileSize()}
|
||||
testfiles[i] = testfile{name: f.Name, length: f.Size}
|
||||
if len(f.Blocks) == 1 {
|
||||
testfiles[i].hash = fmt.Sprintf("%x", f.Blocks[0].Hash)
|
||||
}
|
||||
|
||||
+55
-27
@@ -27,6 +27,8 @@ func init() {
|
||||
factories["external"] = newExternal
|
||||
}
|
||||
|
||||
const unixSpecialChars = "`" + `"'<>;!#$&*? `
|
||||
|
||||
type external struct {
|
||||
command string
|
||||
filesystem fs.Filesystem
|
||||
@@ -68,36 +70,11 @@ func (v external) Archive(filePath string) error {
|
||||
return errors.New("command is empty, please enter a valid command")
|
||||
}
|
||||
|
||||
words, err := shellquote.Split(v.command)
|
||||
cmd, err := v.prepareCommand(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("command is invalid: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
context := map[string]string{
|
||||
"%FOLDER_FILESYSTEM%": string(v.filesystem.Type()),
|
||||
"%FOLDER_PATH%": v.filesystem.URI(),
|
||||
"%FILE_PATH%": filePath,
|
||||
}
|
||||
|
||||
for i, word := range words {
|
||||
for key, val := range context {
|
||||
word = strings.ReplaceAll(word, key, val)
|
||||
}
|
||||
|
||||
words[i] = word
|
||||
}
|
||||
|
||||
cmd := exec.Command(words[0], words[1:]...)
|
||||
env := os.Environ()
|
||||
// filter STGUIAUTH and STGUIAPIKEY from environment variables
|
||||
var filteredEnv []string
|
||||
|
||||
for _, x := range env {
|
||||
if !strings.HasPrefix(x, "STGUIAUTH=") && !strings.HasPrefix(x, "STGUIAPIKEY=") {
|
||||
filteredEnv = append(filteredEnv, x)
|
||||
}
|
||||
}
|
||||
cmd.Env = filteredEnv
|
||||
combinedOutput, err := cmd.CombinedOutput()
|
||||
l.Debugln("external command output:", string(combinedOutput))
|
||||
if err != nil {
|
||||
@@ -126,3 +103,54 @@ func (external) Restore(_ string, _ time.Time) error {
|
||||
func (external) Clean(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareCommand returns the command with environment for the given file
|
||||
// path.
|
||||
func (v external) prepareCommand(filePath string) (*exec.Cmd, error) {
|
||||
words, err := shellquote.Split(v.command)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("command is invalid: %w", err)
|
||||
}
|
||||
|
||||
context := map[string]string{
|
||||
"%FOLDER_FILESYSTEM%": string(v.filesystem.Type()),
|
||||
"%FOLDER_PATH%": v.filesystem.URI(),
|
||||
"%FILE_PATH%": filePath,
|
||||
}
|
||||
|
||||
for i, word := range words {
|
||||
unsafe := strings.ContainsAny(word, unixSpecialChars)
|
||||
for key, val := range context {
|
||||
// If the parameter contains both an unsafe character and a
|
||||
// template placeholder, we consider it unsafe and reject it.
|
||||
// Note that the shell splitting will have already removed outer
|
||||
// quotes, so that a command like `foo "%FILE_PATH%"` is fine
|
||||
// here, despite the double quote being one of our unsafe
|
||||
// characters.
|
||||
if unsafe && strings.Contains(word, key) {
|
||||
return nil, errors.New("unsafe external versioning command; see https://docs.syncthing.net/users/versioning.html#external-file-versioning")
|
||||
}
|
||||
word = strings.ReplaceAll(word, key, val)
|
||||
}
|
||||
|
||||
words[i] = word
|
||||
}
|
||||
|
||||
// filter STGUIAUTH and STGUIAPIKEY from environment variables, and add
|
||||
// our folder info.
|
||||
env := os.Environ()
|
||||
var filteredEnv []string
|
||||
for _, x := range env {
|
||||
if !strings.HasPrefix(x, "STGUIAUTH=") && !strings.HasPrefix(x, "STGUIAPIKEY=") {
|
||||
filteredEnv = append(filteredEnv, x)
|
||||
}
|
||||
}
|
||||
for k, v := range context {
|
||||
k = strings.Trim(k, "%")
|
||||
filteredEnv = append(filteredEnv, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
cmd := exec.Command(words[0], words[1:]...) //nolint:gosec // execution with user tainted data, by design
|
||||
cmd.Env = filteredEnv
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
@@ -76,6 +76,39 @@ func TestExternal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternalCommandSplit(t *testing.T) {
|
||||
e := external{
|
||||
filesystem: fs.NewFilesystem(fs.FilesystemTypeFake, "TestExternalCommandSplit"),
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
cmd string
|
||||
safe bool
|
||||
}{
|
||||
{`echo %FOLDER_PATH% %FILE_PATH%`, true},
|
||||
{`echo "%FOLDER_PATH% %FILE_PATH%"`, false},
|
||||
{`echo %FOLDER_PATH%/%FILE_PATH%`, true},
|
||||
{`echo "%FOLDER_PATH%/%FILE_PATH%"`, true},
|
||||
{`echo '%FOLDER_PATH%/%FILE_PATH%'`, true},
|
||||
{`echo "'%FOLDER_PATH%/%FILE_PATH%'"`, false},
|
||||
{`sh -c "echo '%FOLDER_PATH%/%FILE_PATH%'"`, false},
|
||||
{`sh -c "echo %FOLDER_PATH%/%FILE_PATH%"`, false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
e.command = tc.cmd
|
||||
res, err := e.prepareCommand("evil file name")
|
||||
if tc.safe && err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !tc.safe && err == nil {
|
||||
t.Logf("%q", res.Path)
|
||||
t.Logf("%q", res.Args)
|
||||
t.Errorf("should be unsafe: %q", tc.cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prepForRemoval(t *testing.T, file string) {
|
||||
if err := os.RemoveAll("testdata"); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+1
-1
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
+1
-1
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
+1
-1
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.SH INTRODUCTION AND DEFINITIONS
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.SH OVERVIEW
|
||||
@@ -1528,11 +1528,10 @@ addresses to global discovery.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B sendFullIndexOnUpgrade
|
||||
Controls whether all index data is resent when an upgrade has happened,
|
||||
equivalent to starting Syncthing with \fB\-\-reset\-deltas\fP\&. This used
|
||||
to be the default behavior in older versions, but is mainly useful as a
|
||||
troubleshooting step and causes high database churn. The default is now
|
||||
\fBfalse\fP\&.
|
||||
Controls whether all index data is resent when an upgrade has happened.
|
||||
This used to be the default behavior in older versions, but is mainly useful
|
||||
as a troubleshooting step and causes high database churn. The default is
|
||||
now \fBfalse\fP\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.SH DESCRIPTION
|
||||
|
||||
+1
-1
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.INDENT 0.0
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.SH ANNOUNCEMENTS
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.SH MODE OF OPERATION
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.SH ROUTER SETUP
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-RELAY" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.SH WHAT IS A RELAY?
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.sp
|
||||
@@ -665,8 +665,10 @@ curl \-X POST \-H \(dqX\-API\-Key: abc123\(dq http://localhost:8384/rest/system/
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
\fBCaution\fP: See \fB\-\-reset\-database\fP for \fB\&.stfolder\fP creation
|
||||
side\-effect and caution regarding mountpoints.
|
||||
Creates \fB\&.stfolder\fP folders in each sync folder if they do not already exist.
|
||||
\fBCaution\fP: Ensure that all sync folders which are mountpoints are already
|
||||
mounted. Inconsistent versions may result if the mountpoint is later mounted
|
||||
and contains older versions.
|
||||
.SS POST /rest/system/restart
|
||||
.sp
|
||||
Post with empty body to immediately restart Syncthing.
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING-VERSIONING" "7" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.sp
|
||||
|
||||
+25
-3
@@ -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" "Jun 07, 2026" "v2.1.0" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "Jun 08, 2026" "v2.1.0" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.SH SYNOPSIS
|
||||
@@ -404,14 +404,36 @@ acknowledge (clear) them.
|
||||
.TP
|
||||
.B debug
|
||||
Various tools to aid in diagnosing problems or collection information for
|
||||
bug reports. Some of these commands access the database directly and can
|
||||
therefore only work when Syncthing is not running.
|
||||
bug reports.
|
||||
.TP
|
||||
.B \fB\-\fP (a single dash)
|
||||
Reads subsequent commands from the standard input stream, without needing to
|
||||
call the \fBsyncthing cli\fP command over and over. Exits on any invalid
|
||||
command or when EOF (end\-of\-file) is received.
|
||||
.UNINDENT
|
||||
.sp
|
||||
For troubleshooting issues, the \fBdebug\fP subcommand provides some commonly
|
||||
needed actions for data analysis and repair. Some of these commands access the
|
||||
database directly and can therefore only work when Syncthing is not running.
|
||||
Not to be confused with the \fBcli debug\fP subcommand group, which works via the
|
||||
REST API.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B debug reset\-database
|
||||
Reset the database, forcing a full rescan and resync. \fBMust only be used
|
||||
when Syncthing is not running.\fP More granular reset operations are
|
||||
available on the REST API while Syncthing is running:
|
||||
\fI\%POST /rest/system/reset\fP
|
||||
.TP
|
||||
.B debug database\-statistics
|
||||
Display database size statistics.
|
||||
.TP
|
||||
.B debug database\-counts
|
||||
Display database folder counts.
|
||||
.TP
|
||||
.B debug database\-file
|
||||
Display database file metadata.
|
||||
.UNINDENT
|
||||
.SH PROXIES
|
||||
.sp
|
||||
Syncthing can use a SOCKS, HTTP, or HTTPS proxy to talk to the outside
|
||||
|
||||
Reference in New Issue
Block a user