Script to debug https://forum.syncthing.net/t/findfirstfile-error/12140/3
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