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 	"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.
     19 func 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).
     66 func 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