Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 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 | |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 7 | import ( |
| 8 | "io" |
| 9 | "syscall" |
| 10 | ) |
Russ Cox | 08a073a | 2011-11-01 21:49:08 -0400 | [diff] [blame] | 11 | |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 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. |
Brad Fitzpatrick | 6454a3e | 2012-01-19 15:45:18 -0800 | [diff] [blame] | 19 | func MkdirAll(path string, perm FileMode) error { |
Russ Cox | 1eea5ca | 2014-10-06 15:49:33 -0400 | [diff] [blame] | 20 | // Fast path: if we can tell whether path is a directory or file, stop with success or error. |
Ryan Hitchman | 8959851 | 2010-12-10 10:43:45 +1100 | [diff] [blame] | 21 | dir, err := Stat(path) |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 22 | if err == nil { |
Russ Cox | 8dce57e | 2011-11-30 12:04:16 -0500 | [diff] [blame] | 23 | if dir.IsDir() { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 24 | return nil |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 25 | } |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 26 | return &PathError{"mkdir", path, syscall.ENOTDIR} |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 27 | } |
| 28 | |
Russ Cox | 1eea5ca | 2014-10-06 15:49:33 -0400 | [diff] [blame] | 29 | // Slow path: make sure parent exists and then call Mkdir for path. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 30 | i := len(path) |
Yasuhiro Matsumoto | 0f4510b | 2011-05-29 13:03:49 +1000 | [diff] [blame] | 31 | for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator. |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 32 | i-- |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 33 | } |
| 34 | |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 35 | j := i |
Yasuhiro Matsumoto | 0f4510b | 2011-05-29 13:03:49 +1000 | [diff] [blame] | 36 | for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element. |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 37 | j-- |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 38 | } |
| 39 | |
Albert Strasheim | 492039a | 2011-04-04 15:45:03 -0400 | [diff] [blame] | 40 | if j > 1 { |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 41 | // Create parent |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 42 | err = MkdirAll(path[0:j-1], perm) |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 43 | if err != nil { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 44 | return err |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 45 | } |
| 46 | } |
| 47 | |
Russ Cox | 1eea5ca | 2014-10-06 15:49:33 -0400 | [diff] [blame] | 48 | // Parent now exists; invoke Mkdir and use its result. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 49 | err = Mkdir(path, perm) |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 50 | if err != nil { |
| 51 | // Handle arguments like "foo/." by |
| 52 | // double-checking that directory doesn't exist. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 53 | dir, err1 := Lstat(path) |
Russ Cox | 8dce57e | 2011-11-30 12:04:16 -0500 | [diff] [blame] | 54 | if err1 == nil && dir.IsDir() { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 55 | return nil |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 56 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 57 | return err |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 58 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 59 | return nil |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | // RemoveAll removes path and any children it contains. |
| 63 | // It removes everything it can but returns the first error |
Austin Clements | 3776f31 | 2009-08-05 14:18:54 -0700 | [diff] [blame] | 64 | // it encounters. If the path does not exist, RemoveAll |
| 65 | // returns nil (no error). |
Russ Cox | 08a073a | 2011-11-01 21:49:08 -0400 | [diff] [blame] | 66 | func RemoveAll(path string) error { |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 67 | // Simple case: if Remove works, we're done. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 68 | err := Remove(path) |
Russ Cox | 98a5f52 | 2014-09-18 14:48:47 -0400 | [diff] [blame] | 69 | if err == nil || IsNotExist(err) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 70 | return nil |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | // Otherwise, is this a directory we need to recurse into? |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 74 | dir, serr := Lstat(path) |
Austin Clements | 3776f31 | 2009-08-05 14:18:54 -0700 | [diff] [blame] | 75 | if serr != nil { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 76 | if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 77 | return nil |
Austin Clements | 3776f31 | 2009-08-05 14:18:54 -0700 | [diff] [blame] | 78 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 79 | return serr |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 80 | } |
Russ Cox | 8dce57e | 2011-11-30 12:04:16 -0500 | [diff] [blame] | 81 | if !dir.IsDir() { |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 82 | // Not a directory; return the error from Remove. |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 83 | return err |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | // Directory. |
Rob Pike | 8a90fd3 | 2011-04-04 23:42:14 -0700 | [diff] [blame] | 87 | fd, err := Open(path) |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 88 | if err != nil { |
Brad Fitzpatrick | 82ddcc0 | 2014-09-23 14:26:20 -0700 | [diff] [blame] | 89 | if IsNotExist(err) { |
Brad Fitzpatrick | db492b8 | 2014-09-23 14:55:19 -0700 | [diff] [blame] | 90 | // Race. It was deleted between the Lstat and Open. |
| 91 | // Return nil per RemoveAll's docs. |
Brad Fitzpatrick | 82ddcc0 | 2014-09-23 14:26:20 -0700 | [diff] [blame] | 92 | return nil |
| 93 | } |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 94 | return err |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 95 | } |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 96 | |
| 97 | // Remove contents & return first error. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 98 | err = nil |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 99 | for { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 100 | names, err1 := fd.Readdirnames(100) |
Russ Cox | ca6a0fe | 2009-09-15 09:41:59 -0700 | [diff] [blame] | 101 | for _, name := range names { |
Yasuhiro Matsumoto | 0f4510b | 2011-05-29 13:03:49 +1000 | [diff] [blame] | 102 | err1 := RemoveAll(path + string(PathSeparator) + name) |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 103 | if err == nil { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 104 | err = err1 |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 105 | } |
| 106 | } |
Russ Cox | 08a073a | 2011-11-01 21:49:08 -0400 | [diff] [blame] | 107 | if err1 == io.EOF { |
Brad Fitzpatrick | 4da5cd4 | 2011-05-16 09:26:16 -0700 | [diff] [blame] | 108 | break |
| 109 | } |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 110 | // If Readdirnames returned an error, use it. |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 111 | if err == nil { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 112 | err = err1 |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 113 | } |
| 114 | if len(names) == 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 115 | break |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | |
Alex Brainman | 9997dae | 2010-09-17 12:35:34 +1000 | [diff] [blame] | 119 | // Close directory, because windows won't remove opened directory. |
| 120 | fd.Close() |
| 121 | |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 122 | // Remove directory. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 123 | err1 := Remove(path) |
Russ Cox | 98a5f52 | 2014-09-18 14:48:47 -0400 | [diff] [blame] | 124 | if err1 == nil || IsNotExist(err1) { |
| 125 | return nil |
| 126 | } |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 127 | if err == nil { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 128 | err = err1 |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 129 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 130 | return err |
Russ Cox | 66f5e89 | 2009-05-15 14:11:24 -0700 | [diff] [blame] | 131 | } |