github.com/s1s1ty/go@v0.0.0-20180207192209-104445e3140f/src/os/path.go (about)

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