Handle long filenames on Windows (fixes #1295)

This commit is contained in:
Jakob Borg
2015-04-05 22:52:22 +02:00
parent ff4974178a
commit b2a79855ae
8 changed files with 120 additions and 50 deletions

View File

@@ -15,6 +15,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
@@ -70,7 +71,7 @@ func (orig Configuration) Copy() Configuration {
type FolderConfiguration struct {
ID string `xml:"id,attr" json:"id"`
Path string `xml:"path,attr" json:"path"`
RawPath string `xml:"path,attr" json:"path"`
Devices []FolderDeviceConfiguration `xml:"device" json:"devices"`
ReadOnly bool `xml:"ro,attr" json:"readOnly"`
RescanIntervalS int `xml:"rescanIntervalS,attr" json:"rescanIntervalS"`
@@ -94,9 +95,37 @@ func (orig FolderConfiguration) Copy() FolderConfiguration {
return c
}
func (f FolderConfiguration) Path() string {
// This is intentionally not a pointer method, because things like
// cfg.Folders["default"].Path() should be valid.
// Attempt tilde expansion; leave unchanged in case of error
if path, err := osutil.ExpandTilde(f.RawPath); err == nil {
f.RawPath = path
}
// Attempt absolutification; leave unchanged in case of error
if !filepath.IsAbs(f.RawPath) {
// Abs() looks like a fairly expensive syscall on Windows, while
// IsAbs() is a whole bunch of string mangling. I think IsAbs() may be
// somewhat faster in the general case, hence the outer if...
if path, err := filepath.Abs(f.RawPath); err == nil {
f.RawPath = path
}
}
// Attempt to enable long filename support on Windows. We may still not
// have an absolute path here if the previous steps failed.
if runtime.GOOS == "windows" && filepath.IsAbs(f.RawPath) && !strings.HasPrefix(f.RawPath, `\\`) {
return `\\?\` + f.RawPath
}
return f.RawPath
}
func (f *FolderConfiguration) CreateMarker() error {
if !f.HasMarker() {
marker := filepath.Join(f.Path, ".stfolder")
marker := filepath.Join(f.Path(), ".stfolder")
fd, err := os.Create(marker)
if err != nil {
return err
@@ -109,7 +138,7 @@ func (f *FolderConfiguration) CreateMarker() error {
}
func (f *FolderConfiguration) HasMarker() bool {
_, err := os.Stat(filepath.Join(f.Path, ".stfolder"))
_, err := os.Stat(filepath.Join(f.Path(), ".stfolder"))
if err != nil {
return false
}
@@ -285,7 +314,7 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
for i := range cfg.Folders {
folder := &cfg.Folders[i]
if len(folder.Path) == 0 {
if len(folder.RawPath) == 0 {
folder.Invalid = "no directory configured"
continue
}
@@ -296,7 +325,7 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
// C:\somedir\ -> C:\somedir\\ -> C:\somedir
// This way in the tests, we get away without OS specific separators
// in the test configs.
folder.Path = filepath.Dir(folder.Path + string(filepath.Separator))
folder.RawPath = filepath.Dir(folder.RawPath + string(filepath.Separator))
if folder.ID == "" {
folder.ID = "default"

View File

@@ -11,8 +11,10 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"github.com/syncthing/protocol"
@@ -78,7 +80,7 @@ func TestDeviceConfig(t *testing.T) {
expectedFolders := []FolderConfiguration{
{
ID: "test",
Path: "testdata",
RawPath: "testdata",
Devices: []FolderDeviceConfiguration{{DeviceID: device1}, {DeviceID: device4}},
ReadOnly: true,
RescanIntervalS: 600,
@@ -297,10 +299,10 @@ func TestVersioningConfig(t *testing.T) {
func TestIssue1262(t *testing.T) {
cfg, err := Load("testdata/issue-1262.xml", device4)
if err != nil {
t.Error(err)
t.Fatal(err)
}
actual := cfg.Folders()["test"].Path
actual := cfg.Folders()["test"].RawPath
expected := "e:"
if runtime.GOOS == "windows" {
expected = `e:\`
@@ -311,6 +313,51 @@ func TestIssue1262(t *testing.T) {
}
}
func TestWindowsPaths(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Not useful on non-Windows")
return
}
folder := FolderConfiguration{
RawPath: `e:\`,
}
expected := `\\?\e:\`
actual := folder.Path()
if actual != expected {
t.Errorf("%q != %q", actual, expected)
}
folder.RawPath = `\\192.0.2.22\network\share`
expected = folder.RawPath
actual = folder.Path()
if actual != expected {
t.Errorf("%q != %q", actual, expected)
}
folder.RawPath = `relative\path`
expected = folder.RawPath
actual = folder.Path()
if actual != expected {
t.Errorf("%q != %q", actual, expected)
}
}
func TestFolderPath(t *testing.T) {
folder := FolderConfiguration{
RawPath: "~/tmp",
}
realPath := folder.Path()
if !filepath.IsAbs(realPath) {
t.Error(realPath, "should be absolute")
}
if strings.Contains(realPath, "~") {
t.Error(realPath, "should not contain ~")
}
}
func TestNewSaveLoad(t *testing.T) {
path := "testdata/temp.xml"
os.Remove(path)
@@ -391,8 +438,8 @@ func TestRequiresRestart(t *testing.T) {
newCfg = cfg
newCfg.Folders = append(newCfg.Folders, FolderConfiguration{
ID: "t1",
Path: "t1",
ID: "t1",
RawPath: "t1",
})
if !ChangeRequiresRestart(cfg, newCfg) {
t.Error("Adding a folder requires restart")
@@ -411,7 +458,7 @@ func TestRequiresRestart(t *testing.T) {
if ChangeRequiresRestart(cfg, newCfg) {
t.Error("No changes done yet")
}
newCfg.Folders[0].Path = "different"
newCfg.Folders[0].RawPath = "different"
if !ChangeRequiresRestart(cfg, newCfg) {
t.Error("Changing a folder requires restart")
}

View File

@@ -159,12 +159,6 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration {
if w.folderMap == nil {
w.folderMap = make(map[string]FolderConfiguration, len(w.cfg.Folders))
for _, fld := range w.cfg.Folders {
path, err := osutil.ExpandTilde(fld.Path)
if err != nil {
l.Warnln("home:", err)
continue
}
fld.Path = path
w.folderMap[fld.ID] = fld
}
}