Skip to content

Commit

Permalink
Client.MkdirAll - mimic os.MkdirAll code for consistency & perf
Browse files Browse the repository at this point in the history
  • Loading branch information
urjitbhatia committed Apr 25, 2018
1 parent 9f2445d commit 831654e
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 16 deletions.
51 changes: 36 additions & 15 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package sftp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"path"
"strings"
"sync/atomic"
"syscall"
"time"

"github.com/kr/fs"
Expand Down Expand Up @@ -687,24 +686,46 @@ func (c *Client) Mkdir(path string) error {
// If path is already a directory, MkdirAll does nothing and returns nil.
// If path contains a regular file, an error is returned
func (c *Client) MkdirAll(path string) error {
parts := ""
for _, p := range strings.Split(path, "/") {
if p == "" {
continue
}
parts += "/" + p
dir, err := c.Stat(parts)
if err == nil {
if !dir.IsDir() {
return fmt.Errorf("Found a non-directory file on path: %s", parts)
}
continue
// Most of this code mimics https://golang.org/src/os/path.go?s=514:561#L13
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
dir, err := c.Stat(path)
if err == nil {
if dir.IsDir() {
return nil
}
err = c.Mkdir(parts)
return &os.PathError{Op: "mkdir", Path: path, Err: 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
err = c.MkdirAll(path[0 : j-1])
if err != nil {
return err
}
}

// Parent now exists; invoke Mkdir and use its result.
err = c.Mkdir(path)
if err != nil {
// Handle arguments like "foo/." by
// double-checking that directory doesn't exist.
dir, err1 := c.Lstat(path)
if err1 == nil && dir.IsDir() {
return nil
}
return err
}
return nil
}

Expand Down
6 changes: 5 additions & 1 deletion client_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,13 @@ func TestClientMkdirAll(t *testing.T) {
if err := sftp.MkdirAll(sub); err != nil {
t.Fatal(err)
}
if _, err := os.Lstat(sub); err != nil {
info, err := os.Lstat(sub)
if err != nil {
t.Fatal(err)
}
if !info.IsDir() {
t.Fatalf("Expected mkdirall to create dir at: %s", sub)
}
}

func TestClientOpen(t *testing.T) {
Expand Down

0 comments on commit 831654e

Please sign in to comment.