Home | History | Annotate | Download | only in os
      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 }
    138