github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/containerfs/rm.go (about)

     1  //go:build !darwin && !windows
     2  
     3  package containerfs // import "github.com/Prakhar-Agarwal-byte/moby/pkg/containerfs"
     4  
     5  import (
     6  	"os"
     7  	"syscall"
     8  	"time"
     9  
    10  	"github.com/moby/sys/mount"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
    15  // often be remedied.
    16  // Only use `EnsureRemoveAll` if you really want to make every effort to remove
    17  // a directory.
    18  //
    19  // Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
    20  // can be a race between reading directory entries and then actually attempting
    21  // to remove everything in the directory.
    22  // These types of errors do not need to be returned since it's ok for the dir to
    23  // be gone we can just retry the remove operation.
    24  //
    25  // This should not return a `os.ErrNotExist` kind of error under any circumstances
    26  func EnsureRemoveAll(dir string) error {
    27  	notExistErr := make(map[string]bool)
    28  
    29  	// track retries
    30  	exitOnErr := make(map[string]int)
    31  	maxRetry := 50
    32  
    33  	// Attempt to unmount anything beneath this dir first
    34  	mount.RecursiveUnmount(dir)
    35  
    36  	for {
    37  		err := os.RemoveAll(dir)
    38  		if err == nil {
    39  			return nil
    40  		}
    41  
    42  		pe, ok := err.(*os.PathError)
    43  		if !ok {
    44  			return err
    45  		}
    46  
    47  		if os.IsNotExist(err) {
    48  			if notExistErr[pe.Path] {
    49  				return err
    50  			}
    51  			notExistErr[pe.Path] = true
    52  
    53  			// There is a race where some subdir can be removed but after the parent
    54  			//   dir entries have been read.
    55  			// So the path could be from `os.Remove(subdir)`
    56  			// If the reported non-existent path is not the passed in `dir` we
    57  			// should just retry, but otherwise return with no error.
    58  			if pe.Path == dir {
    59  				return nil
    60  			}
    61  			continue
    62  		}
    63  
    64  		if pe.Err != syscall.EBUSY {
    65  			return err
    66  		}
    67  
    68  		if e := mount.Unmount(pe.Path); e != nil {
    69  			return errors.Wrapf(e, "error while removing %s", dir)
    70  		}
    71  
    72  		if exitOnErr[pe.Path] == maxRetry {
    73  			return err
    74  		}
    75  		exitOnErr[pe.Path]++
    76  		time.Sleep(100 * time.Millisecond)
    77  	}
    78  }