@@ -3,12 +3,11 @@ package sftp
3
3
import (
4
4
"bytes"
5
5
"encoding/binary"
6
- "fmt"
7
6
"io"
8
7
"os"
9
8
"path"
10
- "strings"
11
9
"sync/atomic"
10
+ "syscall"
12
11
"time"
13
12
14
13
"github.com/kr/fs"
@@ -687,24 +686,46 @@ func (c *Client) Mkdir(path string) error {
687
686
// If path is already a directory, MkdirAll does nothing and returns nil.
688
687
// If path contains a regular file, an error is returned
689
688
func (c * Client ) MkdirAll (path string ) error {
690
- parts := ""
691
- for _ , p := range strings .Split (path , "/" ) {
692
- if p == "" {
693
- continue
694
- }
695
- parts += "/" + p
696
- dir , err := c .Stat (parts )
697
- if err == nil {
698
- if ! dir .IsDir () {
699
- return fmt .Errorf ("Found a non-directory file on path: %s" , parts )
700
- }
701
- continue
689
+ // Most of this code mimics https://golang.org/src/os/path.go?s=514:561#L13
690
+ // Fast path: if we can tell whether path is a directory or file, stop with success or error.
691
+ dir , err := c .Stat (path )
692
+ if err == nil {
693
+ if dir .IsDir () {
694
+ return nil
702
695
}
703
- err = c .Mkdir (parts )
696
+ return & os.PathError {Op : "mkdir" , Path : path , Err : syscall .ENOTDIR }
697
+ }
698
+
699
+ // Slow path: make sure parent exists and then call Mkdir for path.
700
+ i := len (path )
701
+ for i > 0 && os .IsPathSeparator (path [i - 1 ]) { // Skip trailing path separator.
702
+ i --
703
+ }
704
+
705
+ j := i
706
+ for j > 0 && ! os .IsPathSeparator (path [j - 1 ]) { // Scan backward over element.
707
+ j --
708
+ }
709
+
710
+ if j > 1 {
711
+ // Create parent
712
+ err = c .MkdirAll (path [0 : j - 1 ])
704
713
if err != nil {
705
714
return err
706
715
}
707
716
}
717
+
718
+ // Parent now exists; invoke Mkdir and use its result.
719
+ err = c .Mkdir (path )
720
+ if err != nil {
721
+ // Handle arguments like "foo/." by
722
+ // double-checking that directory doesn't exist.
723
+ dir , err1 := c .Lstat (path )
724
+ if err1 == nil && dir .IsDir () {
725
+ return nil
726
+ }
727
+ return err
728
+ }
708
729
return nil
709
730
}
710
731
0 commit comments