github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/os/removeall_at.go (about)

     1  // Copyright 2018 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  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package os
     8  
     9  import (
    10  	"internal/syscall/unix"
    11  	"io"
    12  	"runtime"
    13  	"syscall"
    14  )
    15  
    16  func removeAll(path string) error {
    17  	if path == "" {
    18  		// fail silently to retain compatibility with previous behavior
    19  		// of RemoveAll. See issue 28830.
    20  		return nil
    21  	}
    22  
    23  	// The rmdir system call does not permit removing ".",
    24  	// so we don't permit it either.
    25  	if endsWithDot(path) {
    26  		return &PathError{"RemoveAll", path, syscall.EINVAL}
    27  	}
    28  
    29  	// Simple case: if Remove works, we're done.
    30  	err := Remove(path)
    31  	if err == nil || IsNotExist(err) {
    32  		return nil
    33  	}
    34  
    35  	// RemoveAll recurses by deleting the path base from
    36  	// its parent directory
    37  	parentDir, base := splitPath(path)
    38  
    39  	parent, err := Open(parentDir)
    40  	if IsNotExist(err) {
    41  		// If parent does not exist, base cannot exist. Fail silently
    42  		return nil
    43  	}
    44  	if err != nil {
    45  		return err
    46  	}
    47  	defer parent.Close()
    48  
    49  	if err := removeAllFrom(parent, base); err != nil {
    50  		if pathErr, ok := err.(*PathError); ok {
    51  			pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
    52  			err = pathErr
    53  		}
    54  		return err
    55  	}
    56  	return nil
    57  }
    58  
    59  func removeAllFrom(parent *File, base string) error {
    60  	parentFd := int(parent.Fd())
    61  	// Simple case: if Unlink (aka remove) works, we're done.
    62  	err := unix.Unlinkat(parentFd, base, 0)
    63  	if err == nil || IsNotExist(err) {
    64  		return nil
    65  	}
    66  
    67  	// EISDIR means that we have a directory, and we need to
    68  	// remove its contents.
    69  	// EPERM or EACCES means that we don't have write permission on
    70  	// the parent directory, but this entry might still be a directory
    71  	// whose contents need to be removed.
    72  	// Otherwise just return the error.
    73  	if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
    74  		return &PathError{"unlinkat", base, err}
    75  	}
    76  
    77  	// Is this a directory we need to recurse into?
    78  	var statInfo syscall.Stat_t
    79  	statErr := unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
    80  	if statErr != nil {
    81  		if IsNotExist(statErr) {
    82  			return nil
    83  		}
    84  		return &PathError{"fstatat", base, statErr}
    85  	}
    86  	if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
    87  		// Not a directory; return the error from the unix.Unlinkat.
    88  		return &PathError{"unlinkat", base, err}
    89  	}
    90  
    91  	// Remove the directory's entries.
    92  	var recurseErr error
    93  	for {
    94  		const request = 1024
    95  
    96  		// Open the directory to recurse into
    97  		file, err := openFdAt(parentFd, base)
    98  		if err != nil {
    99  			if IsNotExist(err) {
   100  				return nil
   101  			}
   102  			recurseErr = &PathError{"openfdat", base, err}
   103  			break
   104  		}
   105  
   106  		names, readErr := file.Readdirnames(request)
   107  		// Errors other than EOF should stop us from continuing.
   108  		if readErr != nil && readErr != io.EOF {
   109  			file.Close()
   110  			if IsNotExist(readErr) {
   111  				return nil
   112  			}
   113  			return &PathError{"readdirnames", base, readErr}
   114  		}
   115  
   116  		for _, name := range names {
   117  			err := removeAllFrom(file, name)
   118  			if err != nil {
   119  				if pathErr, ok := err.(*PathError); ok {
   120  					pathErr.Path = base + string(PathSeparator) + pathErr.Path
   121  				}
   122  				recurseErr = err
   123  			}
   124  		}
   125  
   126  		// Removing files from the directory may have caused
   127  		// the OS to reshuffle it. Simply calling Readdirnames
   128  		// again may skip some entries. The only reliable way
   129  		// to avoid this is to close and re-open the
   130  		// directory. See issue 20841.
   131  		file.Close()
   132  
   133  		// Finish when the end of the directory is reached
   134  		if len(names) < request {
   135  			break
   136  		}
   137  	}
   138  
   139  	// Remove the directory itself.
   140  	unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
   141  	if unlinkError == nil || IsNotExist(unlinkError) {
   142  		return nil
   143  	}
   144  
   145  	if recurseErr != nil {
   146  		return recurseErr
   147  	}
   148  	return &PathError{"unlinkat", base, unlinkError}
   149  }
   150  
   151  // openFdAt opens path relative to the directory in fd.
   152  // Other than that this should act like openFileNolog.
   153  // This acts like openFileNolog rather than OpenFile because
   154  // we are going to (try to) remove the file.
   155  // The contents of this file are not relevant for test caching.
   156  func openFdAt(dirfd int, name string) (*File, error) {
   157  	var r int
   158  	for {
   159  		var e error
   160  		r, e = unix.Openat(dirfd, name, O_RDONLY, 0)
   161  		if e == nil {
   162  			break
   163  		}
   164  
   165  		// See comment in openFileNolog.
   166  		if runtime.GOOS == "darwin" && e == syscall.EINTR {
   167  			continue
   168  		}
   169  
   170  		return nil, e
   171  	}
   172  
   173  	if !supportsCloseOnExec {
   174  		syscall.CloseOnExec(r)
   175  	}
   176  
   177  	return newFile(uintptr(r), name, kindOpenFile), nil
   178  }