diff --git a/_demo/mkdirdemo/mkdir.go b/_demo/mkdirdemo/mkdir.go new file mode 100644 index 00000000..cf9ea4cc --- /dev/null +++ b/_demo/mkdirdemo/mkdir.go @@ -0,0 +1,11 @@ +package main + +import ( + "os" + "path/filepath" +) + +func main() { + dirPath := filepath.Join("temp", "myapp", "data", "logs") + os.MkdirAll(dirPath, 0755) +} diff --git a/internal/lib/os/os.go b/internal/lib/os/os.go index cf609934..ebc0b46a 100644 --- a/internal/lib/os/os.go +++ b/internal/lib/os/os.go @@ -271,7 +271,6 @@ func Mkdir(name string, perm FileMode) error { */ // TODO(xsw): -// func MkdirAll(path string, perm FileMode) error // func NewSyscallError(syscall string, err error) error // func ReadFile(name string) ([]byte, error) diff --git a/internal/lib/os/path.go b/internal/lib/os/path.go new file mode 100644 index 00000000..aee2266c --- /dev/null +++ b/internal/lib/os/path.go @@ -0,0 +1,55 @@ +package os + +import ( + "syscall" +) + +// MkdirAll creates a directory named path, +// along with any necessary parents, and returns nil, +// or else returns an error. +// The permission bits perm (before umask) 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 FileMode) error { + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &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 && IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent. + err = MkdirAll(fixRootDirectory(path[:j-1]), perm) + if err != nil { + return err + } + } + + // Parent now exists; invoke Mkdir and use its result. + err = Mkdir(path, perm) + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +}