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
5package os
6
7import (
8	"io"
9	"syscall"
10)
11
12// MkdirAll creates a directory named path,
13// along with any necessary parents, and returns nil,
14// or else returns an error.
15// The permission bits perm are used for all
16// directories that MkdirAll creates.
17// If path is already a directory, MkdirAll does nothing
18// and returns nil.
19func MkdirAll(path string, perm FileMode) error {
20	// Fast path: if we can tell whether path is a directory or file, stop with success or error.
21	dir, err := Stat(path)
22	if err == nil {
23		if dir.IsDir() {
24			return nil
25		}
26		return &PathError{"mkdir", path, syscall.ENOTDIR}
27	}
28
29	// Slow path: make sure parent exists and then call Mkdir for path.
30	i := len(path)
31	for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator.
32		i--
33	}
34
35	j := i
36	for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element.
37		j--
38	}
39
40	if j > 1 {
41		// Create parent
42		err = MkdirAll(path[0:j-1], perm)
43		if err != nil {
44			return err
45		}
46	}
47
48	// Parent now exists; invoke Mkdir and use its result.
49	err = Mkdir(path, perm)
50	if err != nil {
51		// Handle arguments like "foo/." by
52		// double-checking that directory doesn't exist.
53		dir, err1 := Lstat(path)
54		if err1 == nil && dir.IsDir() {
55			return nil
56		}
57		return err
58	}
59	return nil
60}
61
62// RemoveAll removes path and any children it contains.
63// It removes everything it can but returns the first error
64// it encounters.  If the path does not exist, RemoveAll
65// returns nil (no error).
66func RemoveAll(path string) error {
67	// Simple case: if Remove works, we're done.
68	err := Remove(path)
69	if err == nil || IsNotExist(err) {
70		return nil
71	}
72
73	// Otherwise, is this a directory we need to recurse into?
74	dir, serr := Lstat(path)
75	if serr != nil {
76		if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
77			return nil
78		}
79		return serr
80	}
81	if !dir.IsDir() {
82		// Not a directory; return the error from Remove.
83		return err
84	}
85
86	// Directory.
87	fd, err := Open(path)
88	if err != nil {
89		if IsNotExist(err) {
90			// Race. It was deleted between the Lstat and Open.
91			// Return nil per RemoveAll's docs.
92			return nil
93		}
94		return err
95	}
96
97	// Remove contents & return first error.
98	err = nil
99	for {
100		names, err1 := fd.Readdirnames(100)
101		for _, name := range names {
102			err1 := RemoveAll(path + string(PathSeparator) + name)
103			if err == nil {
104				err = err1
105			}
106		}
107		if err1 == io.EOF {
108			break
109		}
110		// If Readdirnames returned an error, use it.
111		if err == nil {
112			err = err1
113		}
114		if len(names) == 0 {
115			break
116		}
117	}
118
119	// Close directory, because windows won't remove opened directory.
120	fd.Close()
121
122	// Remove directory.
123	err1 := Remove(path)
124	if err1 == nil || IsNotExist(err1) {
125		return nil
126	}
127	if err == nil {
128		err = err1
129	}
130	return err
131}
132