1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package os 6 7 import ( 8 "syscall" 9 ) 10 11 // MkdirAll creates a directory named path, 12 // along with any necessary parents, and returns nil, 13 // or else returns an error. 14 // The permission bits perm (before umask) are used for all 15 // directories that MkdirAll creates. 16 // If path is already a directory, MkdirAll does nothing 17 // and returns nil. 18 func MkdirAll(path string, perm FileMode) error { 19 // Fast path: if we can tell whether path is a directory or file, stop with success or error. 20 dir, err := Stat(path) 21 if err == nil { 22 if dir.IsDir() { 23 return nil 24 } 25 return &PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} 26 } 27 28 // Slow path: make sure parent exists and then call Mkdir for path. 29 30 // Extract the parent folder from path by first removing any trailing 31 // path separator and then scanning backward until finding a path 32 // separator or reaching the beginning of the string. 33 i := len(path) - 1 34 for i >= 0 && IsPathSeparator(path[i]) { 35 i-- 36 } 37 for i >= 0 && !IsPathSeparator(path[i]) { 38 i-- 39 } 40 if i < 0 { 41 i = 0 42 } 43 44 // If there is a parent directory, and it is not the volume name, 45 // recurse to ensure parent directory exists. 46 if parent := path[:i]; len(parent) > len(volumeName(path)) { 47 err = MkdirAll(parent, perm) 48 if err != nil { 49 return err 50 } 51 } 52 53 // Parent now exists; invoke Mkdir and use its result. 54 err = Mkdir(path, perm) 55 if err != nil { 56 // Handle arguments like "foo/." by 57 // double-checking that directory doesn't exist. 58 dir, err1 := Lstat(path) 59 if err1 == nil && dir.IsDir() { 60 return nil 61 } 62 return err 63 } 64 return nil 65 } 66 67 // RemoveAll removes path and any children it contains. 68 // It removes everything it can but returns the first error 69 // it encounters. If the path does not exist, RemoveAll 70 // returns nil (no error). 71 // If there is an error, it will be of type *PathError. 72 func RemoveAll(path string) error { 73 return removeAll(path) 74 } 75 76 // endsWithDot reports whether the final component of path is ".". 77 func endsWithDot(path string) bool { 78 if path == "." { 79 return true 80 } 81 if len(path) >= 2 && path[len(path)-1] == '.' && IsPathSeparator(path[len(path)-2]) { 82 return true 83 } 84 return false 85 } 86