blob: 84a3be3348f1d82e28790a4d005c857fce938ee0 [file] [log] [blame]
Russ Cox66f5e892009-05-15 14:11:24 -07001// 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
Rob Pike56069f02012-02-17 10:04:29 +11007import (
8 "io"
9 "syscall"
10)
Russ Cox08a073a2011-11-01 21:49:08 -040011
Russ Cox66f5e892009-05-15 14:11:24 -070012// 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.
Brad Fitzpatrick6454a3e2012-01-19 15:45:18 -080019func MkdirAll(path string, perm FileMode) error {
Russ Cox1eea5ca2014-10-06 15:49:33 -040020 // Fast path: if we can tell whether path is a directory or file, stop with success or error.
Ryan Hitchman89598512010-12-10 10:43:45 +110021 dir, err := Stat(path)
Russ Cox66f5e892009-05-15 14:11:24 -070022 if err == nil {
Russ Cox8dce57e2011-11-30 12:04:16 -050023 if dir.IsDir() {
Robert Griesemer40621d52009-11-09 12:07:39 -080024 return nil
Russ Cox66f5e892009-05-15 14:11:24 -070025 }
Rob Pike56069f02012-02-17 10:04:29 +110026 return &PathError{"mkdir", path, syscall.ENOTDIR}
Russ Cox66f5e892009-05-15 14:11:24 -070027 }
28
Russ Cox1eea5ca2014-10-06 15:49:33 -040029 // Slow path: make sure parent exists and then call Mkdir for path.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080030 i := len(path)
Yasuhiro Matsumoto0f4510b2011-05-29 13:03:49 +100031 for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator.
Robert Griesemer40621d52009-11-09 12:07:39 -080032 i--
Russ Cox66f5e892009-05-15 14:11:24 -070033 }
34
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080035 j := i
Yasuhiro Matsumoto0f4510b2011-05-29 13:03:49 +100036 for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element.
Robert Griesemer40621d52009-11-09 12:07:39 -080037 j--
Russ Cox66f5e892009-05-15 14:11:24 -070038 }
39
Albert Strasheim492039a2011-04-04 15:45:03 -040040 if j > 1 {
Russ Cox66f5e892009-05-15 14:11:24 -070041 // Create parent
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080042 err = MkdirAll(path[0:j-1], perm)
Russ Cox66f5e892009-05-15 14:11:24 -070043 if err != nil {
Robert Griesemer40621d52009-11-09 12:07:39 -080044 return err
Russ Cox66f5e892009-05-15 14:11:24 -070045 }
46 }
47
Russ Cox1eea5ca2014-10-06 15:49:33 -040048 // Parent now exists; invoke Mkdir and use its result.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080049 err = Mkdir(path, perm)
Russ Cox66f5e892009-05-15 14:11:24 -070050 if err != nil {
51 // Handle arguments like "foo/." by
52 // double-checking that directory doesn't exist.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080053 dir, err1 := Lstat(path)
Russ Cox8dce57e2011-11-30 12:04:16 -050054 if err1 == nil && dir.IsDir() {
Robert Griesemer40621d52009-11-09 12:07:39 -080055 return nil
Russ Cox66f5e892009-05-15 14:11:24 -070056 }
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080057 return err
Russ Cox66f5e892009-05-15 14:11:24 -070058 }
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080059 return nil
Russ Cox66f5e892009-05-15 14:11:24 -070060}
61
62// RemoveAll removes path and any children it contains.
63// It removes everything it can but returns the first error
Austin Clements3776f312009-08-05 14:18:54 -070064// it encounters. If the path does not exist, RemoveAll
65// returns nil (no error).
Russ Cox08a073a2011-11-01 21:49:08 -040066func RemoveAll(path string) error {
Russ Cox66f5e892009-05-15 14:11:24 -070067 // Simple case: if Remove works, we're done.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080068 err := Remove(path)
Russ Cox98a5f522014-09-18 14:48:47 -040069 if err == nil || IsNotExist(err) {
Robert Griesemer40621d52009-11-09 12:07:39 -080070 return nil
Russ Cox66f5e892009-05-15 14:11:24 -070071 }
72
73 // Otherwise, is this a directory we need to recurse into?
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080074 dir, serr := Lstat(path)
Austin Clements3776f312009-08-05 14:18:54 -070075 if serr != nil {
Rob Pike56069f02012-02-17 10:04:29 +110076 if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
Robert Griesemer40621d52009-11-09 12:07:39 -080077 return nil
Austin Clements3776f312009-08-05 14:18:54 -070078 }
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080079 return serr
Russ Cox66f5e892009-05-15 14:11:24 -070080 }
Russ Cox8dce57e2011-11-30 12:04:16 -050081 if !dir.IsDir() {
Russ Cox66f5e892009-05-15 14:11:24 -070082 // Not a directory; return the error from Remove.
Robert Griesemer40621d52009-11-09 12:07:39 -080083 return err
Russ Cox66f5e892009-05-15 14:11:24 -070084 }
85
86 // Directory.
Rob Pike8a90fd32011-04-04 23:42:14 -070087 fd, err := Open(path)
Russ Cox66f5e892009-05-15 14:11:24 -070088 if err != nil {
Brad Fitzpatrick82ddcc02014-09-23 14:26:20 -070089 if IsNotExist(err) {
Brad Fitzpatrickdb492b82014-09-23 14:55:19 -070090 // Race. It was deleted between the Lstat and Open.
91 // Return nil per RemoveAll's docs.
Brad Fitzpatrick82ddcc02014-09-23 14:26:20 -070092 return nil
93 }
Robert Griesemer40621d52009-11-09 12:07:39 -080094 return err
Russ Cox66f5e892009-05-15 14:11:24 -070095 }
Russ Cox66f5e892009-05-15 14:11:24 -070096
97 // Remove contents & return first error.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -080098 err = nil
Russ Cox66f5e892009-05-15 14:11:24 -070099 for {
Robert Griesemerd65a5cc2009-12-15 15:40:16 -0800100 names, err1 := fd.Readdirnames(100)
Russ Coxca6a0fe2009-09-15 09:41:59 -0700101 for _, name := range names {
Yasuhiro Matsumoto0f4510b2011-05-29 13:03:49 +1000102 err1 := RemoveAll(path + string(PathSeparator) + name)
Russ Coxa0bcaf42009-06-25 20:24:55 -0700103 if err == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800104 err = err1
Russ Cox66f5e892009-05-15 14:11:24 -0700105 }
106 }
Russ Cox08a073a2011-11-01 21:49:08 -0400107 if err1 == io.EOF {
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700108 break
109 }
Russ Cox66f5e892009-05-15 14:11:24 -0700110 // If Readdirnames returned an error, use it.
Russ Coxa0bcaf42009-06-25 20:24:55 -0700111 if err == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800112 err = err1
Russ Cox66f5e892009-05-15 14:11:24 -0700113 }
114 if len(names) == 0 {
Robert Griesemer40621d52009-11-09 12:07:39 -0800115 break
Russ Cox66f5e892009-05-15 14:11:24 -0700116 }
117 }
118
Alex Brainman9997dae2010-09-17 12:35:34 +1000119 // Close directory, because windows won't remove opened directory.
120 fd.Close()
121
Russ Cox66f5e892009-05-15 14:11:24 -0700122 // Remove directory.
Robert Griesemerd65a5cc2009-12-15 15:40:16 -0800123 err1 := Remove(path)
Russ Cox98a5f522014-09-18 14:48:47 -0400124 if err1 == nil || IsNotExist(err1) {
125 return nil
126 }
Russ Coxa0bcaf42009-06-25 20:24:55 -0700127 if err == nil {
Robert Griesemer40621d52009-11-09 12:07:39 -0800128 err = err1
Russ Cox66f5e892009-05-15 14:11:24 -0700129 }
Robert Griesemerd65a5cc2009-12-15 15:40:16 -0800130 return err
Russ Cox66f5e892009-05-15 14:11:24 -0700131}