mirror of
https://github.com/rclone/rclone.git
synced 2025-01-29 21:04:23 +02:00
Fix windows build - fixes #628
Try to make clearer the distinction between OS paths and rclone paths (remotes) so it is harder to muddle them up.
This commit is contained in:
parent
84eb7031bb
commit
6089f443b9
@ -15,6 +15,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@ -41,7 +43,7 @@ func init() {
|
|||||||
// Fs represents a local filesystem rooted at root
|
// Fs represents a local filesystem rooted at root
|
||||||
type Fs struct {
|
type Fs struct {
|
||||||
name string // the name of the remote
|
name string // the name of the remote
|
||||||
root string // The root directory
|
root string // The root directory (OS path)
|
||||||
precisionOk sync.Once // Whether we need to read the precision
|
precisionOk sync.Once // Whether we need to read the precision
|
||||||
precision time.Duration // precision of local filesystem
|
precision time.Duration // precision of local filesystem
|
||||||
wmu sync.Mutex // used for locking access to 'warned'.
|
wmu sync.Mutex // used for locking access to 'warned'.
|
||||||
@ -70,7 +72,7 @@ func NewFs(name, root string) (fs.Fs, error) {
|
|||||||
warned: make(map[string]struct{}),
|
warned: make(map[string]struct{}),
|
||||||
nounc: nounc == "true",
|
nounc: nounc == "true",
|
||||||
}
|
}
|
||||||
f.root = f.filterPath(root)
|
f.root = f.cleanPath(root)
|
||||||
|
|
||||||
// Check to see if this points to a file
|
// Check to see if this points to a file
|
||||||
fi, err := os.Lstat(f.root)
|
fi, err := os.Lstat(f.root)
|
||||||
@ -100,9 +102,8 @@ func (f *Fs) String() string {
|
|||||||
|
|
||||||
// newObject makes a half completed Object
|
// newObject makes a half completed Object
|
||||||
func (f *Fs) newObject(remote string) *Object {
|
func (f *Fs) newObject(remote string) *Object {
|
||||||
remote = normString(remote)
|
dstPath := f.cleanPath(filepath.Join(f.root, remote))
|
||||||
dstPath := f.filterPath(filepath.Join(f.root, remote))
|
remote = f.cleanRemote(remote)
|
||||||
remote = filepath.ToSlash(f.cleanUtf8(remote))
|
|
||||||
return &Object{
|
return &Object{
|
||||||
fs: f,
|
fs: f,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
@ -175,7 +176,7 @@ func (f *Fs) list(out fs.ListOpts, remote string, dirpath string, level int) (su
|
|||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
if out.IncludeDirectory(newRemote) {
|
if out.IncludeDirectory(newRemote) {
|
||||||
dir := &fs.Dir{
|
dir := &fs.Dir{
|
||||||
Name: normString(f.cleanUtf8(newRemote)),
|
Name: f.cleanRemote(newRemote),
|
||||||
When: fi.ModTime(),
|
When: fi.ModTime(),
|
||||||
Bytes: 0,
|
Bytes: 0,
|
||||||
Count: 0,
|
Count: 0,
|
||||||
@ -207,8 +208,8 @@ func (f *Fs) list(out fs.ListOpts, remote string, dirpath string, level int) (su
|
|||||||
// Ignores everything which isn't Storable, eg links etc
|
// Ignores everything which isn't Storable, eg links etc
|
||||||
func (f *Fs) List(out fs.ListOpts, dir string) {
|
func (f *Fs) List(out fs.ListOpts, dir string) {
|
||||||
defer out.Finished()
|
defer out.Finished()
|
||||||
dir = filterFragment(f.cleanUtf8(dir))
|
root := f.cleanPath(filepath.Join(f.root, dir))
|
||||||
root := filepath.Join(f.root, dir)
|
dir = f.cleanRemote(dir)
|
||||||
_, err := os.Stat(root)
|
_, err := os.Stat(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.SetError(fs.ErrorDirNotFound)
|
out.SetError(fs.ErrorDirNotFound)
|
||||||
@ -252,10 +253,11 @@ func (f *Fs) List(out fs.ListOpts, dir string) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUtf8 makes string a valid UTF-8 string
|
// cleanRemote makes string a valid UTF-8 string for remote strings.
|
||||||
//
|
//
|
||||||
// Any invalid UTF-8 characters will be replaced with utf8.RuneError
|
// Any invalid UTF-8 characters will be replaced with utf8.RuneError
|
||||||
func (f *Fs) cleanUtf8(name string) string {
|
// It also normalises the UTF-8 and converts the slashes if necessary.
|
||||||
|
func (f *Fs) cleanRemote(name string) string {
|
||||||
if !utf8.ValidString(name) {
|
if !utf8.ValidString(name) {
|
||||||
f.wmu.Lock()
|
f.wmu.Lock()
|
||||||
if _, ok := f.warned[name]; !ok {
|
if _, ok := f.warned[name]; !ok {
|
||||||
@ -265,9 +267,8 @@ func (f *Fs) cleanUtf8(name string) string {
|
|||||||
f.wmu.Unlock()
|
f.wmu.Unlock()
|
||||||
name = string([]rune(name))
|
name = string([]rune(name))
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "windows" {
|
name = norm.NFC.String(name)
|
||||||
name = cleanWindowsName(f, name)
|
name = filepath.ToSlash(name)
|
||||||
}
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,8 +648,8 @@ func (o *Object) Remove() error {
|
|||||||
return os.Remove(o.path)
|
return os.Remove(o.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the current directory and file from a path
|
// Return the directory and file from an OS path. Assumes
|
||||||
// Assumes os.PathSeparator is used.
|
// os.PathSeparator is used.
|
||||||
func getDirFile(s string) (string, string) {
|
func getDirFile(s string) (string, string) {
|
||||||
i := strings.LastIndex(s, string(os.PathSeparator))
|
i := strings.LastIndex(s, string(os.PathSeparator))
|
||||||
dir, file := s[:i], s[i+1:]
|
dir, file := s[:i], s[i+1:]
|
||||||
@ -658,9 +659,9 @@ func getDirFile(s string) (string, string) {
|
|||||||
return dir, file
|
return dir, file
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterFragment cleans a path fragment which is part of a bigger
|
// cleanPathFragment cleans an OS path fragment which is part of a
|
||||||
// path and not necessarily absolute
|
// bigger path and not necessarily absolute
|
||||||
func filterFragment(s string) string {
|
func cleanPathFragment(s string) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -671,11 +672,16 @@ func filterFragment(s string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterPath cleans and makes absolute the path passed in.
|
// cleanPath cleans and makes absolute the path passed in and returns
|
||||||
|
// an OS path.
|
||||||
//
|
//
|
||||||
// On windows it makes the path UNC also.
|
// The input might be in OS form or rclone form or a mixture, but the
|
||||||
func (f *Fs) filterPath(s string) string {
|
// output is in OS form.
|
||||||
s = filterFragment(s)
|
//
|
||||||
|
// On windows it makes the path UNC also and replaces any characters
|
||||||
|
// Windows can't deal with with their replacements.
|
||||||
|
func (f *Fs) cleanPath(s string) string {
|
||||||
|
s = cleanPathFragment(s)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if !filepath.IsAbs(s) && !strings.HasPrefix(s, "\\") {
|
if !filepath.IsAbs(s) && !strings.HasPrefix(s, "\\") {
|
||||||
s2, err := filepath.Abs(s)
|
s2, err := filepath.Abs(s)
|
||||||
@ -683,21 +689,19 @@ func (f *Fs) filterPath(s string) string {
|
|||||||
s = s2
|
s = s2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !f.nounc {
|
||||||
if f.nounc {
|
// Convert to UNC
|
||||||
return s
|
s = uncPath(s)
|
||||||
}
|
}
|
||||||
// Convert to UNC
|
s = cleanWindowsName(f, s)
|
||||||
return uncPath(s)
|
} else {
|
||||||
}
|
if !filepath.IsAbs(s) {
|
||||||
|
s2, err := filepath.Abs(s)
|
||||||
if !filepath.IsAbs(s) {
|
if err == nil {
|
||||||
s2, err := filepath.Abs(s)
|
s = s2
|
||||||
if err == nil {
|
}
|
||||||
s = s2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +730,7 @@ func uncPath(s string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanWindowsName will clean invalid Windows characters
|
// cleanWindowsName will clean invalid Windows characters replacing them with _
|
||||||
func cleanWindowsName(f *Fs, name string) string {
|
func cleanWindowsName(f *Fs, name string) string {
|
||||||
original := name
|
original := name
|
||||||
var name2 string
|
var name2 string
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
// +build darwin
|
|
||||||
|
|
||||||
package local
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// normString normalises the remote name as some OS X denormalises UTF-8 when storing it to disk
|
|
||||||
func normString(remote string) string {
|
|
||||||
return norm.NFC.String(remote)
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
// +build !darwin
|
|
||||||
|
|
||||||
package local
|
|
||||||
|
|
||||||
// normString normalises the remote name if necessary
|
|
||||||
func normString(remote string) string {
|
|
||||||
return remote
|
|
||||||
}
|
|
@ -57,11 +57,11 @@ var utf8Tests = [][2]string{
|
|||||||
{string([]byte{'a', 0x80, 'b'}), "a�b"},
|
{string([]byte{'a', 0x80, 'b'}), "a�b"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCleanUtf8(t *testing.T) {
|
func TestCleanRemote(t *testing.T) {
|
||||||
f := &Fs{}
|
f := &Fs{}
|
||||||
f.warned = make(map[string]struct{})
|
f.warned = make(map[string]struct{})
|
||||||
for _, test := range utf8Tests {
|
for _, test := range utf8Tests {
|
||||||
got := f.cleanUtf8(test[0])
|
got := f.cleanRemote(test[0])
|
||||||
expect := test[1]
|
expect := test[1]
|
||||||
if got != expect {
|
if got != expect {
|
||||||
t.Fatalf("got %q, expected %q", got, expect)
|
t.Fatalf("got %q, expected %q", got, expect)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user