mirror of
https://github.com/octoleo/syncthing.git
synced 2025-01-03 15:17:25 +00:00
Revert "Case insensitive renames, part 1"
This commit is contained in:
parent
460cb19839
commit
be2ca0ea22
@ -1370,7 +1370,6 @@ nextSub:
|
|||||||
// TODO: We should limit the Have scanning to start at sub
|
// TODO: We should limit the Have scanning to start at sub
|
||||||
seenPrefix := false
|
seenPrefix := false
|
||||||
var iterError error
|
var iterError error
|
||||||
css := osutil.NewCachedCaseSensitiveStat(folderCfg.Path())
|
|
||||||
fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
|
fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
|
||||||
f := fi.(db.FileInfoTruncated)
|
f := fi.(db.FileInfoTruncated)
|
||||||
hasPrefix := len(subs) == 0
|
hasPrefix := len(subs) == 0
|
||||||
@ -1414,7 +1413,7 @@ nextSub:
|
|||||||
Version: f.Version, // The file is still the same, so don't bump version
|
Version: f.Version, // The file is still the same, so don't bump version
|
||||||
}
|
}
|
||||||
batch = append(batch, nf)
|
batch = append(batch, nf)
|
||||||
} else if _, err := css.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
|
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
|
||||||
// File has been deleted.
|
// File has been deleted.
|
||||||
|
|
||||||
// We don't specifically verify that the error is
|
// We don't specifically verify that the error is
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -242,90 +241,3 @@ func SetTCPOptions(conn *net.TCPConn) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The CachedCaseSensitiveStat provides an Lstat() method similar to
|
|
||||||
// os.Lstat(), but that is always case sensitive regardless of underlying file
|
|
||||||
// system semantics. The "Cached" part refers to the fact that it lists the
|
|
||||||
// contents of a directory the first time it's needed and then retains this
|
|
||||||
// information for the duration. It's expected that instances of this type are
|
|
||||||
// fairly short lived.
|
|
||||||
//
|
|
||||||
// There's some song and dance to check directories that are parents to the
|
|
||||||
// checked path as well, that is we want to catch the situation that someone
|
|
||||||
// calls Lstat("foo/BAR/baz") when the actual path is "foo/bar/baz" and return
|
|
||||||
// NotExist appropriately. But we don't want to do this check too high up, as
|
|
||||||
// the user may have told us the folder path is ~/Sync while it is actually
|
|
||||||
// ~/sync and this *should* work properly... Sigh. Hence the "base" parameter.
|
|
||||||
type CachedCaseSensitiveStat struct {
|
|
||||||
base string // base directory, we should not check stuff above this
|
|
||||||
results map[string][]os.FileInfo // directory path => list of children
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCachedCaseSensitiveStat(base string) *CachedCaseSensitiveStat {
|
|
||||||
return &CachedCaseSensitiveStat{
|
|
||||||
base: strings.ToLower(base),
|
|
||||||
results: make(map[string][]os.FileInfo),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CachedCaseSensitiveStat) Lstat(name string) (os.FileInfo, error) {
|
|
||||||
dir := filepath.Dir(name)
|
|
||||||
base := filepath.Base(name)
|
|
||||||
|
|
||||||
if !strings.HasPrefix(strings.ToLower(dir), c.base) {
|
|
||||||
// We only validate things within the base directory, which we need to
|
|
||||||
// compare case insensitively against.
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't already have a list of directory entries for this
|
|
||||||
// directory, try to list it. Return error if this fails.
|
|
||||||
l, ok := c.results[dir]
|
|
||||||
if !ok {
|
|
||||||
if len(dir) > len(c.base) {
|
|
||||||
// We are checking in a subdirectory of base. Must make sure *it*
|
|
||||||
// exists with the specified casing, up to the base directory.
|
|
||||||
if _, err := c.Lstat(dir); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, err := os.Open(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer fd.Close()
|
|
||||||
|
|
||||||
l, err = fd.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(fileInfoList(l))
|
|
||||||
c.results[dir] = l
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the index of the first entry with name >= base using binary search.
|
|
||||||
idx := sort.Search(len(l), func(i int) bool {
|
|
||||||
return l[i].Name() >= base
|
|
||||||
})
|
|
||||||
|
|
||||||
if idx >= len(l) || l[idx].Name() != base {
|
|
||||||
// The search didn't find any such entry
|
|
||||||
return nil, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return l[idx], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileInfoList []os.FileInfo
|
|
||||||
|
|
||||||
func (l fileInfoList) Len() int {
|
|
||||||
return len(l)
|
|
||||||
}
|
|
||||||
func (l fileInfoList) Swap(a, b int) {
|
|
||||||
l[a], l[b] = l[b], l[a]
|
|
||||||
}
|
|
||||||
func (l fileInfoList) Less(a, b int) bool {
|
|
||||||
return l[a].Name() < l[b].Name()
|
|
||||||
}
|
|
||||||
|
@ -7,11 +7,8 @@
|
|||||||
package osutil_test
|
package osutil_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/osutil"
|
"github.com/syncthing/syncthing/lib/osutil"
|
||||||
@ -182,78 +179,3 @@ func TestDiskUsage(t *testing.T) {
|
|||||||
t.Error("Disk is full?", free)
|
t.Error("Disk is full?", free)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCaseSensitiveStat(t *testing.T) {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows", "darwin":
|
|
||||||
break // We can test!
|
|
||||||
default:
|
|
||||||
t.Skip("Cannot test on this platform")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "TestCaseSensitiveStat")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(dir, "File"), []byte("data"), 0644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Lstat(filepath.Join(dir, "File")); err != nil {
|
|
||||||
// Standard Lstat should report the file exists
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
if _, err := os.Lstat(filepath.Join(dir, "fILE")); err != nil {
|
|
||||||
// ... also with the incorrect case spelling
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the case sensitive stat:er. We stress it a little by giving it a
|
|
||||||
// base path with an intentionally incorrect casing.
|
|
||||||
|
|
||||||
css := osutil.NewCachedCaseSensitiveStat(strings.ToUpper(dir))
|
|
||||||
|
|
||||||
if _, err := css.Lstat(filepath.Join(dir, "File")); err != nil {
|
|
||||||
// Our Lstat should report the file exists
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
if _, err := css.Lstat(filepath.Join(dir, "fILE")); err == nil || !os.IsNotExist(err) {
|
|
||||||
// ... but with the incorrect case we should get ErrNotExist
|
|
||||||
t.Fatal("Unexpected non-IsNotExist error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now do the same tests for a file in a case-sensitive directory.
|
|
||||||
|
|
||||||
if err := os.Mkdir(filepath.Join(dir, "Dir"), 0755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(filepath.Join(dir, "Dir/File"), []byte("data"), 0644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Lstat(filepath.Join(dir, "Dir/File")); err != nil {
|
|
||||||
// Standard Lstat should report the file exists
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
if _, err := os.Lstat(filepath.Join(dir, "dIR/File")); err != nil {
|
|
||||||
// ... also with the incorrect case spelling
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recreate the case sensitive stat:er. We stress it a little by giving it a
|
|
||||||
// base path with an intentionally incorrect casing.
|
|
||||||
|
|
||||||
css = osutil.NewCachedCaseSensitiveStat(strings.ToLower(dir))
|
|
||||||
|
|
||||||
if _, err := css.Lstat(filepath.Join(dir, "Dir/File")); err != nil {
|
|
||||||
// Our Lstat should report the file exists
|
|
||||||
t.Fatal("Unexpected error:", err)
|
|
||||||
}
|
|
||||||
if _, err := css.Lstat(filepath.Join(dir, "dIR/File")); err == nil || !os.IsNotExist(err) {
|
|
||||||
// ... but with the incorrect case we should get ErrNotExist
|
|
||||||
t.Fatal("Unexpected non-IsNotExist error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user