// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris

package os

import (
	
	
	
)

func removeAll( string) error {
	if  == "" {
		// fail silently to retain compatibility with previous behavior
		// of RemoveAll. See issue 28830.
		return nil
	}

	// The rmdir system call does not permit removing ".",
	// so we don't permit it either.
	if endsWithDot() {
		return &PathError{Op: "RemoveAll", Path: , Err: syscall.EINVAL}
	}

	// Simple case: if Remove works, we're done.
	 := Remove()
	if  == nil || IsNotExist() {
		return nil
	}

	// RemoveAll recurses by deleting the path base from
	// its parent directory
	,  := splitPath()

	,  := Open()
	if IsNotExist() {
		// If parent does not exist, base cannot exist. Fail silently
		return nil
	}
	if  != nil {
		return 
	}
	defer .Close()

	if  := removeAllFrom(, );  != nil {
		if ,  := .(*PathError);  {
			.Path =  + string(PathSeparator) + .Path
			 = 
		}
		return 
	}
	return nil
}

func removeAllFrom( *File,  string) error {
	 := int(.Fd())
	// Simple case: if Unlink (aka remove) works, we're done.
	 := unix.Unlinkat(, , 0)
	if  == nil || IsNotExist() {
		return nil
	}

	// EISDIR means that we have a directory, and we need to
	// remove its contents.
	// EPERM or EACCES means that we don't have write permission on
	// the parent directory, but this entry might still be a directory
	// whose contents need to be removed.
	// Otherwise just return the error.
	if  != syscall.EISDIR &&  != syscall.EPERM &&  != syscall.EACCES {
		return &PathError{Op: "unlinkat", Path: , Err: }
	}

	// Is this a directory we need to recurse into?
	var  syscall.Stat_t
	 := unix.Fstatat(, , &, unix.AT_SYMLINK_NOFOLLOW)
	if  != nil {
		if IsNotExist() {
			return nil
		}
		return &PathError{Op: "fstatat", Path: , Err: }
	}
	if .Mode&syscall.S_IFMT != syscall.S_IFDIR {
		// Not a directory; return the error from the unix.Unlinkat.
		return &PathError{Op: "unlinkat", Path: , Err: }
	}

	// Remove the directory's entries.
	var  error
	for {
		const  = 1024
		var  int

		// Open the directory to recurse into
		,  := openFdAt(, )
		if  != nil {
			if IsNotExist() {
				return nil
			}
			 = &PathError{Op: "openfdat", Path: , Err: }
			break
		}

		for {
			 := 0

			,  := .Readdirnames()
			// Errors other than EOF should stop us from continuing.
			if  != nil &&  != io.EOF {
				.Close()
				if IsNotExist() {
					return nil
				}
				return &PathError{Op: "readdirnames", Path: , Err: }
			}

			 = len()
			for ,  := range  {
				 := (, )
				if  != nil {
					if ,  := .(*PathError);  {
						.Path =  + string(PathSeparator) + .Path
					}
					++
					if  == nil {
						 = 
					}
				}
			}

			// If we can delete any entry, break to start new iteration.
			// Otherwise, we discard current names, get next entries and try deleting them.
			if  !=  {
				break
			}
		}

		// Removing files from the directory may have caused
		// the OS to reshuffle it. Simply calling Readdirnames
		// again may skip some entries. The only reliable way
		// to avoid this is to close and re-open the
		// directory. See issue 20841.
		.Close()

		// Finish when the end of the directory is reached
		if  <  {
			break
		}
	}

	// Remove the directory itself.
	 := unix.Unlinkat(, , unix.AT_REMOVEDIR)
	if  == nil || IsNotExist() {
		return nil
	}

	if  != nil {
		return 
	}
	return &PathError{Op: "unlinkat", Path: , Err: }
}

// openFdAt opens path relative to the directory in fd.
// Other than that this should act like openFileNolog.
// This acts like openFileNolog rather than OpenFile because
// we are going to (try to) remove the file.
// The contents of this file are not relevant for test caching.
func openFdAt( int,  string) (*File, error) {
	var  int
	for {
		var  error
		,  = unix.Openat(, , O_RDONLY|syscall.O_CLOEXEC, 0)
		if  == nil {
			break
		}

		// See comment in openFileNolog.
		if  == syscall.EINTR {
			continue
		}

		return nil, 
	}

	if !supportsCloseOnExec {
		syscall.CloseOnExec()
	}

	return newFile(uintptr(), , kindOpenFile), nil
}