Repository: syncthing/syncthing (language: go)

Issue:
Incorrect handling of path for auto accepted folder
There's a problem in the logic for auto-accepted folders. The idea is that the folder should be placed in a subdirectory to the "default folder path", with the subdirectory name generated from the folder label or ID, in that order. We ensure that this path doesn't exist before starting to use it. However,

1. if the label (or sanitised ID) is empty, Syncthing will try to use the default folder path itself
2. if we're auto-accepting multiple folders at the same time, we might add two or more with the same path, because the folder path isn't created until later when the folder is started.

By default, the default folder path is the user home directory which is very likely to already exist (or Syncthing is likely to be unable to create it), and if it's something else it's guaranteed to exist once a folder has been accepted. This should make point one a fairly rare problem "in the wild". Point two is a problem only when multiple folders with identical labels are shared simultaneously.


Discussion / hints:

Fix patch:
diff --git a/lib/model/model.go b/lib/model/model.go
index b5b9cf3e01d..2b4a58a6184 100644
--- a/lib/model/model.go
+++ b/lib/model/model.go
@@ -1667,16 +1667,31 @@ func (*model) handleDeintroductions(introducerCfg config.DeviceConfiguration, fo
 func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Folder, ccDeviceInfos *clusterConfigDeviceInfo, cfg config.FolderConfiguration, haveCfg bool, defaultPath string) (config.FolderConfiguration, bool) {
 	if !haveCfg {
 		defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath)
-		pathAlternatives := []string{
-			fs.SanitizePath(folder.Label),
-			fs.SanitizePath(folder.ID),
+		var pathAlternatives []string
+		if alt := fs.SanitizePath(folder.Label); alt != "" {
+			pathAlternatives = append(pathAlternatives, alt)
+		}
+		if alt := fs.SanitizePath(folder.ID); alt != "" {
+			pathAlternatives = append(pathAlternatives, alt)
+		}
+		if len(pathAlternatives) == 0 {
+			l.Infof("Failed to auto-accept folder %s from %s due to lack of path alternatives", folder.Description(), deviceID)
+			return config.FolderConfiguration{}, false
 		}
 		for _, path := range pathAlternatives {
+			// Make sure the folder path doesn't already exist.
 			if _, err := defaultPathFs.Lstat(path); !fs.IsNotExist(err) {
 				continue
 			}
 
-			fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, fs.FilesystemTypeBasic, filepath.Join(defaultPath, path))
+			// Attempt to create it to make sure it does, now.
+			fullPath := filepath.Join(defaultPath, path)
+			if err := defaultPathFs.MkdirAll(path, 0o700); err != nil {
+				l.Warnf("Failed to create path for auto-accepted folder %s at path %s: %v", folder.Description(), fullPath, err)
+				continue
+			}
+
+			fcfg := newFolderConfiguration(m.cfg, folder.ID, folder.Label, fs.FilesystemTypeBasic, fullPath)
 			fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{
 				DeviceID: deviceID,
 			})


Test patch: