Last active
September 26, 2018 10:05
-
-
Save imsodin/eab9a292367f86a80e48c38222b0ebea to your computer and use it in GitHub Desktop.
Script to debug https://forum.syncthing.net/t/findfirstfile-error/12140/3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"os" | |
"path/filepath" | |
"runtime" | |
"strings" | |
) | |
// Enter the full path to the affected folder here | |
var folderRoot string = `` | |
func main() { | |
fmt.Println("folderRoot:", folderRoot) | |
// The reason it's done like this: | |
// C: -> C:\ -> C:\ (issue that this is trying to fix) | |
// C:\somedir -> C:\somedir\ -> C:\somedir | |
// C:\somedir\ -> C:\somedir\\ -> C:\somedir | |
// This way in the tests, we get away without OS specific separators | |
// in the test configs. | |
sep := string(filepath.Separator) | |
folderRoot = filepath.Dir(folderRoot + sep) | |
fmt.Println("After drive hack, folderRoot:", folderRoot) | |
// Attempt tilde expansion; leave unchanged in case of error | |
path, err := ExpandTilde(folderRoot) | |
fmt.Println("ExpandTilde:", path, err) | |
if err == nil { | |
folderRoot = path | |
} | |
// Attempt absolutification; leave unchanged in case of error | |
if !filepath.IsAbs(folderRoot) { | |
// Abs() looks like a fairly expensive syscall on Windows, while | |
// IsAbs() is a whole bunch of string mangling. I think IsAbs() may be | |
// somewhat faster in the general case, hence the outer if... | |
path, err := filepath.Abs(folderRoot) | |
fmt.Println("filepath.Abs:", path, err) | |
if err == nil { | |
folderRoot = path | |
} | |
} else { | |
fmt.Println("folderRoot is absolute") | |
} | |
longRoot := longFilenameSupport(folderRoot) | |
fmt.Println("After longFileNameSupport, longRoot:", longRoot) | |
evalRoot, err := filepath.EvalSymlinks(longRoot) | |
if err != nil { | |
fmt.Println("EvalSymlinks on", longRoot, "failed:", err) | |
path, err = filepath.EvalSymlinks(folderRoot) | |
fmt.Println("EvalSymlinks on", folderRoot, "failed:", err) | |
return | |
} | |
fmt.Println("After EvalSymlinks, evalRoot:", evalRoot) | |
evalRoot = longFilenameSupport(evalRoot) | |
fmt.Println("After longFileNameSupport, evalRoot:", evalRoot) | |
absName, err := rooted("", evalRoot) | |
fmt.Println(`rooted("", %v): %v, %v`, evalRoot, absName, err) | |
} | |
// longFilenameSupport adds the necessary prefix to the path to enable long | |
// filename support on windows if necessary. | |
// This does NOT check the current system, i.e. will also take effect on unix paths. | |
func longFilenameSupport(path string) string { | |
if filepath.IsAbs(path) && !strings.HasPrefix(path, `\\`) { | |
return `\\?\` + path | |
} | |
return path | |
} | |
func rooted(rel, root string) (string, error) { | |
// The root must not be empty. | |
if root == "" { | |
return "", ErrInvalidFilename | |
} | |
var err error | |
// Takes care that rel does not try to escape | |
rel, err = Canonicalize(rel) | |
if err != nil { | |
return "", err | |
} | |
return filepath.Join(root, rel), nil | |
} | |
var errNoHome = errors.New("no home directory found - set $HOME (or the platform equivalent)") | |
func ExpandTilde(path string) (string, error) { | |
if path == "~" { | |
return getHomeDir() | |
} | |
path = filepath.FromSlash(path) | |
if !strings.HasPrefix(path, fmt.Sprintf("~%c", PathSeparator)) { | |
return path, nil | |
} | |
home, err := getHomeDir() | |
if err != nil { | |
return "", err | |
} | |
return filepath.Join(home, path[2:]), nil | |
} | |
func getHomeDir() (string, error) { | |
var home string | |
switch runtime.GOOS { | |
case "windows": | |
home = filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath")) | |
if home == "" { | |
home = os.Getenv("UserProfile") | |
} | |
default: | |
home = os.Getenv("HOME") | |
} | |
if home == "" { | |
return "", errNoHome | |
} | |
return home, nil | |
} | |
var ( | |
ErrInvalidFilename = errors.New("filename is invalid") | |
ErrNotRelative = errors.New("not a relative path") | |
) | |
// Canonicalize checks that the file path is valid and returns it in the "canonical" form: | |
// - /foo/bar -> foo/bar | |
// - / -> "." | |
func Canonicalize(file string) (string, error) { | |
pathSep := string(PathSeparator) | |
if strings.HasPrefix(file, pathSep+pathSep) { | |
// The relative path may pretend to be an absolute path within | |
// the root, but the double path separator on Windows implies | |
// something else and is out of spec. | |
return "", ErrNotRelative | |
} | |
// The relative path should be clean from internal dotdots and similar | |
// funkyness. | |
file = filepath.Clean(file) | |
// It is not acceptable to attempt to traverse upwards. | |
switch file { | |
case "..": | |
return "", ErrNotRelative | |
} | |
if strings.HasPrefix(file, ".."+pathSep) { | |
return "", ErrNotRelative | |
} | |
if strings.HasPrefix(file, pathSep) { | |
if file == pathSep { | |
return ".", nil | |
} | |
return file[1:], nil | |
} | |
return file, nil | |
} | |
const PathSeparator = os.PathSeparator |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment