diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 2e0693ccd..ec755a36b 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -279,7 +279,7 @@ func main() { l.Fatalln(dir, "is not a directory") } if err != nil && os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = osutil.MkdirAll(dir, 0700) if err != nil { l.Fatalln("generate:", err) } @@ -882,7 +882,7 @@ func discovery(extPort int) *discover.Discoverer { func ensureDir(dir string, mode int) { fi, err := os.Stat(dir) if os.IsNotExist(err) { - err := os.MkdirAll(dir, 0700) + err := osutil.MkdirAll(dir, 0700) if err != nil { l.Fatalln(err) } diff --git a/internal/model/model.go b/internal/model/model.go index 76164bb4c..52f553e66 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -1611,7 +1611,7 @@ func (m *Model) CheckFolderHealth(id string) error { } else if os.IsNotExist(err) { // If we don't have any files in the index, and the directory // doesn't exist, try creating it. - err = os.MkdirAll(folder.Path(), 0700) + err = osutil.MkdirAll(folder.Path(), 0700) if err == nil { err = folder.CreateMarker() } diff --git a/internal/osutil/mkdirall.go b/internal/osutil/mkdirall.go new file mode 100644 index 000000000..cba93d050 --- /dev/null +++ b/internal/osutil/mkdirall.go @@ -0,0 +1,17 @@ +// Copyright (C) 2015 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// +build !windows + +package osutil + +import ( + "os" +) + +func MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} diff --git a/internal/osutil/mkdirall_windows.go b/internal/osutil/mkdirall_windows.go new file mode 100644 index 000000000..762632774 --- /dev/null +++ b/internal/osutil/mkdirall_windows.go @@ -0,0 +1,65 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Modified by Zillode to fix https://github.com/syncthing/syncthing/issues/1822 +// Sync with https://github.com/golang/go/blob/master/src/os/path.go +// See https://github.com/golang/go/issues/10900 + +package osutil + +import ( + "os" + "path/filepath" + "syscall" +) + +// MkdirAll creates a directory named path, along with any necessary parents, +// and returns nil, or else returns an error. +// The permission bits perm are used for all directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing and returns nil. +func MkdirAll(path string, perm os.FileMode) error { + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := os.Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{"mkdir", path, syscall.ENOTDIR} + } + + // Slow path: make sure parent exists and then call Mkdir for path. + i := len(path) + for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent + parent := path[0 : j-1] + if parent != filepath.VolumeName(parent) { + err = MkdirAll(parent, perm) + if err != nil { + return err + } + } + } + + // Parent now exists; invoke Mkdir and use its result. + err = os.Mkdir(path, perm) + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} diff --git a/internal/scanner/walk_test.go b/internal/scanner/walk_test.go index d7dd3423c..8aa85e9e3 100644 --- a/internal/scanner/walk_test.go +++ b/internal/scanner/walk_test.go @@ -19,6 +19,7 @@ import ( "github.com/syncthing/protocol" "github.com/syncthing/syncthing/internal/ignore" + "github.com/syncthing/syncthing/internal/osutil" "golang.org/x/text/unicode/norm" ) @@ -218,7 +219,7 @@ func TestNormalization(t *testing.T) { for _, s1 := range tests { // Create a directory for each of the interesting strings above - if err := os.MkdirAll(filepath.Join("testdata/normalization", s1), 0755); err != nil { + if err := osutil.MkdirAll(filepath.Join("testdata/normalization", s1), 0755); err != nil { t.Fatal(err) } diff --git a/internal/versioner/simple.go b/internal/versioner/simple.go index d5dc9750c..15bb9d2a0 100644 --- a/internal/versioner/simple.go +++ b/internal/versioner/simple.go @@ -61,7 +61,7 @@ func (v Simple) Archive(filePath string) error { if debug { l.Debugln("creating versions dir", versionsDir) } - os.MkdirAll(versionsDir, 0755) + osutil.MkdirAll(versionsDir, 0755) osutil.HideFile(versionsDir) } else { return err @@ -79,7 +79,7 @@ func (v Simple) Archive(filePath string) error { } dir := filepath.Join(versionsDir, inFolderPath) - err = os.MkdirAll(dir, 0755) + err = osutil.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err } diff --git a/internal/versioner/staggered.go b/internal/versioner/staggered.go index ca1908fa6..8b0d415a4 100644 --- a/internal/versioner/staggered.go +++ b/internal/versioner/staggered.go @@ -257,7 +257,7 @@ func (v Staggered) Archive(filePath string) error { if debug { l.Debugln("creating versions dir", v.versionsPath) } - os.MkdirAll(v.versionsPath, 0755) + osutil.MkdirAll(v.versionsPath, 0755) osutil.HideFile(v.versionsPath) } else { return err @@ -275,7 +275,7 @@ func (v Staggered) Archive(filePath string) error { } dir := filepath.Join(v.versionsPath, inFolderPath) - err = os.MkdirAll(dir, 0755) + err = osutil.MkdirAll(dir, 0755) if err != nil && !os.IsExist(err) { return err }