From e1b7bf77010352e70df58daacfc9b786437a2db8 Mon Sep 17 00:00:00 2001 From: URenko <18209292+URenko@users.noreply.github.com> Date: Thu, 9 May 2024 18:50:08 +0000 Subject: [PATCH] local: fix encoding of root path fix #7824 Statements like rclone copy . will spontaneously miss if . expands to a path with a Full Width replacement character. This is due to the incorrect order in which relative paths and decoding were handled in the original implementation. --- backend/local/local.go | 55 +++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/backend/local/local.go b/backend/local/local.go index 80f44c76e..e292e689a 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -1568,32 +1568,47 @@ func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error { } func cleanRootPath(s string, noUNC bool, enc encoder.MultiEncoder) string { - if runtime.GOOS != "windows" || !strings.HasPrefix(s, "\\") { - if !filepath.IsAbs(s) { - s2, err := filepath.Abs(s) - if err == nil { - s = s2 - } - } else { - s = filepath.Clean(s) - } - } + var vol string if runtime.GOOS == "windows" { - s = filepath.ToSlash(s) - vol := filepath.VolumeName(s) + vol = filepath.VolumeName(s) if vol == `\\?` && len(s) >= 6 { // `\\?\C:` vol = s[:6] } - s = vol + enc.FromStandardPath(s[len(vol):]) - s = filepath.FromSlash(s) - if !noUNC { - // Convert to UNC - s = file.UNCPath(s) - } - return s + s = s[len(vol):] + } + // Don't use FromStandardPath. Make sure Dot (`.`, `..`) as name will not be reencoded + // Take care of the case Standard: ././‛. (the first dot means current directory) + if enc != encoder.Standard { + s = filepath.ToSlash(s) + parts := strings.Split(s, "/") + encoded := make([]string, len(parts)) + changed := false + for i, p := range parts { + if (p == ".") || (p == "..") { + encoded[i] = p + continue + } + part := enc.FromStandardName(p) + changed = changed || part != p + encoded[i] = part + } + if changed { + s = strings.Join(encoded, "/") + } + s = filepath.FromSlash(s) + } + if runtime.GOOS == "windows" { + s = vol + s + } + s2, err := filepath.Abs(s) + if err == nil { + s = s2 + } + if !noUNC { + // Convert to UNC. It does nothing on non windows platforms. + s = file.UNCPath(s) } - s = enc.FromStandardPath(s) return s }