Skip to content

path/filepath: Rel() returns invalid path if volume case is different #10802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pavel-main opened this issue May 13, 2015 · 11 comments
Closed

path/filepath: Rel() returns invalid path if volume case is different #10802

pavel-main opened this issue May 13, 2015 · 11 comments

Comments

@pavel-main
Copy link

Go version - go1.4.2 windows/amd64
Operating system - Windows 7 64-bit

The problem:

package main

import (
    "path/filepath"
)

func main() {
    relpath, err := filepath.Rel(`E:\Projects\`, `e:\Projects\src`)
    if err != nil {
        println(err.Error())
        return
    }
    println(relpath)
}

Output:

Rel: can't make \Projects\src relative to \Projects

Expected:

src

I've ocassionally found this when some go program's os.Getcwd() call returned me a path with lowercased volume name - not sure why this happened, can't reproduce anymore. Some users, who set their GOPATH to something like e:\Projects might have problems.

Probably, on Windows, VolumeName() should have separate implementation like this:

func VolumeName(path string) string {
    if !isUNC(path) {
        return strings.ToUpper(path[:volumeNameLen(path)])
    }
    return path[:volumeNameLen(path)]
}

Helpful link - Naming Files, Paths, and Namespaces

@alexbrainman
Copy link
Member

We can fix drive letter comparison. But the problem is more general - windows paths are not case sensitive. So, for example, filepath.Rel(E:\Projects\, E:\projects\src) will be wrong too. I am not sure it is worth fixing particular case of drive letter. What do you think?

filepath.Rel is also broken when we walking from one drive to another. It has no meaning for something like filepath.Rel(c:\a\, d:\a\b). I would just avoid using filepath.Rel on windows.

Alex

@pavel-main
Copy link
Author

Hi, Alex!

I've encountered this call in some command-line app, and I guess, a lot of filesystem-related command line apps are already using or will use this necessary call. Is it really ok to abandon this, when so simple solution is in the air?

filepath.Rel(E:\Projects, E:\projects\src) will be wrong too.

But you can compare their both lower or uppercase versions. The only problem - before returning the result you'll have to return the substring in original case - it will might be a clunky solution, but still possible.

It has no meaning for something like filepath.Rel(c:\a, d:\a\b)

I think it would be trivial to compare both drive letters / isUNC before actual comparsion. So, in your specific example, it would be absolutely ok to return "Rel can't make ..." error.

@alexbrainman
Copy link
Member

Fair enough. We can do as much as we can. @kpashka what you like to try and send this change?

Alex

@rsc
Copy link
Contributor

rsc commented Jun 29, 2015

It may be worth fixing this to help the go command (#11409). What is the definition of case-insensitive on Windows? Is it ASCII only, or are c:\δ and c:\Δ the same file name too?

@alexbrainman
Copy link
Member

What is the definition of case-insensitive on Windows?

I really don't know. I use English Windows version. For me everything is ASCII.

Is it ASCII only, or are c:\δ and c:\Δ the same file name too?

U:\>type main.go
package main

import (
        "io/ioutil"
        "log"
        "os"
        "path/filepath"
        "strings"
)

func main() {
        tmpdir, err := ioutil.TempDir("", "alex")
        if err != nil {
                log.Fatal(err)
        }
        defer os.RemoveAll(tmpdir)

        const filename = "\u0441\u0430\u0448\u0430_\u03b4.txt"

        err = ioutil.WriteFile(filepath.Join(tmpdir, filename), nil, 0644)
        if err != nil {
                log.Fatal(err)
        }

        f, err := os.Open(filepath.Join(tmpdir, strings.ToUpper(filename)))
        if err != nil {
                log.Fatal(err)
        }
        defer f.Close()
}

U:\>go run main.go

U:\>

It may be worth fixing this to help the go command (#11409).

This issue is easy, because drive letters are always 'a' to 'z' - there is no unicode here. You were always against treating windows paths as case-incensitive. So I stopped arguing. Maybe we should start ignoring case in drive letter and see where it gets us. I was fighting similar battle (see issue #11459) just the other day.

Mind you this issue is about filepath.Rel. filepath.Rel coubld be overkill for #11409?

Alex

@rsc rsc modified the milestones: Go1.6, Go1.5Maybe Jul 15, 2015
@AngangGuo
Copy link

From filepath.Rel:

That is, Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself

But in Windows I got an error as shown here:
https://play.golang.org/p/uoltpVJeng

Andrew

@gopherbot
Copy link
Contributor

CL https://golang.org/cl/16795 mentions this issue.

@alexbrainman
Copy link
Member

@AngangGuo I don't see your problem is the same as stated in this issue. If you think you have a problem, you should create new issue.

Alex

@amkgo
Copy link

amkgo commented Nov 11, 2015

Thanks. I've create a new issue here:
#13208

Andrew

@rsc rsc closed this as completed in 935faf3 Nov 12, 2015
@0xmohit
Copy link
Contributor

0xmohit commented Nov 13, 2015

Oddly enough, something like

filepath.Rel(`C:\tmp`, `C:\TMP`)

hangs on my Win7 box using tip. The problem doesn't happen with Go 1.5.1. Adding a couple of tests:

diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index 057aa6a..3a11ec4 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -1034,6 +1034,8 @@ var winreltests = []RelTests{
        {`C:\`, `D:\`, `err`},
        {`C:`, `D:`, `err`},
        {`C:\Projects`, `c:\projects\src`, `src`},
+       {`C:\Projects`, `C:\projects`, `.`},
+       {`C:\Projects`, `c:\projects`, `.`},
 }

 func TestRel(t *testing.T) {

cause path tests to timeout after 6m0s when executing all.bat.

@alexbrainman
Copy link
Member

It sure does. Filled issue #13258. Thank you.

Alex

@golang golang locked and limited conversation to collaborators Nov 16, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants