fix(db): handle path names that include URL special chars (fixes #10245) (#10247)

😬
This commit is contained in:
Jakob Borg
2025-08-13 13:01:16 +02:00
committed by GitHub
parent 9ea6c9c3c3
commit 370bbb8f26
2 changed files with 61 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ import (
"database/sql"
"embed"
"io/fs"
"net/url"
"path/filepath"
"strconv"
"strings"
@@ -44,7 +45,12 @@ type baseDB struct {
func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScripts []string) (*baseDB, error) {
// Open the database with options to enable foreign keys and recursive
// triggers (needed for the delete+insert triggers on row replace).
sqlDB, err := sqlx.Open(dbDriver, "file:"+path+"?"+commonOptions)
pathURL := url.URL{
Scheme: "file",
Path: fileToUriPath(path),
RawQuery: commonOptions,
}
sqlDB, err := sqlx.Open(dbDriver, pathURL.String())
if err != nil {
return nil, wrap(err)
}
@@ -110,6 +116,16 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
return db, nil
}
func fileToUriPath(path string) string {
path = filepath.ToSlash(path)
if (build.IsWindows && len(path) >= 2 && path[1] == ':') ||
(strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///")) {
// Add an extra leading slash for Windows drive letter or UNC path
path = "/" + path
}
return path
}
func (s *baseDB) Close() error {
s.updateLock.Lock()
s.statementsMut.Lock()

View File

@@ -12,6 +12,8 @@ import (
"encoding/binary"
"errors"
"iter"
"os"
"path"
"path/filepath"
"sync"
"testing"
@@ -20,6 +22,7 @@ import (
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/itererr"
"github.com/syncthing/syncthing/internal/timeutil"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -1157,6 +1160,47 @@ func TestStrangeDeletedGlobalBug(t *testing.T) {
}
}
func TestOpenSpecialName(t *testing.T) {
dir := t.TempDir()
// Create a "base" dir that is in the way if the path becomes
// incorrectly truncated in the next steps.
base := path.Join(dir, "test")
if err := os.Mkdir(base, 0o755); err != nil {
t.Fatal(err)
}
// Should be able to open a path with a hash sign in it.
p1 := base + "#foo"
db, err := Open(p1)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
if !build.IsWindows {
// Should be able to open a path with something that looks like
// query params.
p2 := base + "?foo=bar"
db, err = Open(p2)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
}
// Better not a have problem with a single ampersand either.
p2 := base + "&foo"
db, err = Open(p2)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
}
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 {