Skip to content

syscall, os: Chmod doesn't support long paths on Windows #20829

Closed
@ibrasho

Description

@ibrasho

What version of Go are you using (go version)?

go version go1.8.3 windows/amd64

What operating system and processor architecture are you using (go env)?

GOOS=windows
GOARCH=amd64

What did you do?

os.Chmod returns an unexpected error when passed a path that is longer than MAX_PATH (260) characters.

What did you expect to see?

Similar behavior to other file functions in os. Calling os.fixLongPath on the path before passing the pointer to GetFileAttributesW and SetFileAttributesW.

231aa9d fixed the issue with long paths (>=260 chars) by introducing a new function, os.fixLongPath, which converts the path to an extended-length path on Windows.

What did you see instead?

chmod C:\Users\ibrahim\go\src\github.com\ibrasho\deptest\vendor\github.com\prometheus\procfs\sysfs\fixtures.src\devices\pci0000_@colon@_00\0000_@colon@_00_@colon@_0d.0\ata4\host3\target3_@colon@_0_@colon@_0\3_@colon@_0_@colon@_0_@colon@_0\block\sdb\bcache\dirty_data: The system cannot find the path specified.

Applying the same logic from os.fixLongPath before passing the path to os.Chmod resolves this issue.

Activity

ibrasho

ibrasho commented on Jun 28, 2017

@ibrasho
ContributorAuthor

I've found this issue while investigating golang/dep#774.

I've found multiple other file related functions implemented outside os (in syscall) that are also affected. According to Naming Files, Paths and Namespaces on MSDN:

These are the directory management functions that no longer have MAX_PATH restrictions if you opt-in to long path behavior: CreateDirectoryW, CreateDirectoryExW GetCurrentDirectoryW RemoveDirectoryW SetCurrentDirectoryW.
These are the file management functions that no longer have MAX_PATH restrictions if you opt-in to long path behavior: CopyFileW, CopyFile2, CopyFileExW, CreateFileW, CreateFile2, CreateHardLinkW, CreateSymbolicLinkW, DeleteFileW, FindFirstFileW, FindFirstFileExW, FindNextFileW, GetFileAttributesW, GetFileAttributesExW, SetFileAttributesW, GetFullPathNameW, GetLongPathNameW, MoveFileW, MoveFileExW, MoveFileWithProgressW, ReplaceFileW, SearchPathW, FindFirstFileNameW, FindNextFileNameW, FindFirstStreamW, FindNextStreamW, GetCompressedFileSizeW, GetFinalPathNameByHandleW.

Here is a list of possible occurrences of this issue (where these functions were called without running the path through os.fixLongPath first:

It could possibly occur in os.SameFile since it ends up calling os.*fileStat.loadFileId which attempts CreateFileW without using os.fixLongPath.

bradfitz

bradfitz commented on Jun 28, 2017

@bradfitz
Contributor

I think we previously decided that we aren't adding magic UNC path mangling to the low-level syscall package. If users are using syscall, they can read MSDN docs.

Let's keep this bug specifically about Chmod.

ibrasho

ibrasho commented on Jun 28, 2017

@ibrasho
ContributorAuthor

I'm can work on a fix. But I've some questions:

  • Since these functions are implemented in syscall, how are they exposed in os? Can we just create a wrapper around them in os that fixes this bug? only 2 of them need fixing.
  • syscall.Rename: is duplicated in internal/syscall/windows.Rename. Is there a reason?

edit: Actually, there is a difference. internal/syscall/windows.Rename replaces the destination if it exists, while syscall.Rename fails in that case.

ibrasho

ibrasho commented on Jun 28, 2017

@ibrasho
ContributorAuthor

@bradfitz In that case, even os.Remove needs the same fix. I'll submit a CL in a couple of minutes.

Edit: my mistake. I was reading the wrong file. Only os.Chmod is broken.

bradfitz

bradfitz commented on Jun 28, 2017

@bradfitz
Contributor

Remove was already done in 231aa9d. I see:

// Remove removes the named file or directory.                                                                                                                                                                  
// If there is an error, it will be of type *PathError.                                                                                                                                                         
func Remove(name string) error {                                                                       
        p, e := syscall.UTF16PtrFromString(fixLongPath(name))                                          
        if e != nil {                                                                                  
                return &PathError{"remove", name, e}                                                   
        }                                                                                              
...

And Chmod should be just:

diff --git a/src/os/file_posix.go b/src/os/file_posix.go
index 6ee7eeb..5ac0acd 100644
--- a/src/os/file_posix.go
+++ b/src/os/file_posix.go
@@ -48,7 +48,7 @@ func syscallMode(i FileMode) (o uint32) {
 // If the file is a symbolic link, it changes the mode of the link's target.
 // If there is an error, it will be of type *PathError.
 func Chmod(name string, mode FileMode) error {
-       if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
+       if e := syscall.Chmod(fixLongPath(name), syscallMode(mode)); e != nil {
                return &PathError{"chmod", name, e}
        }
        return nil
gopherbot

gopherbot commented on Jun 28, 2017

@gopherbot
Contributor

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

locked and limited conversation to collaborators on Jul 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @bradfitz@ibrasho@gopherbot

        Issue actions

          syscall, os: Chmod doesn't support long paths on Windows · Issue #20829 · golang/go