github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/pkg/fsutils/fsutils_linux.go (about)

     1  package fsutils // import "github.com/docker/docker/pkg/fsutils"
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"unsafe"
     8  
     9  	"golang.org/x/sys/unix"
    10  )
    11  
    12  func locateDummyIfEmpty(path string) (string, error) {
    13  	children, err := ioutil.ReadDir(path)
    14  	if err != nil {
    15  		return "", err
    16  	}
    17  	if len(children) != 0 {
    18  		return "", nil
    19  	}
    20  	dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
    21  	if err != nil {
    22  		return "", err
    23  	}
    24  	name := dummyFile.Name()
    25  	err = dummyFile.Close()
    26  	return name, err
    27  }
    28  
    29  // SupportsDType returns whether the filesystem mounted on path supports d_type
    30  func SupportsDType(path string) (bool, error) {
    31  	// locate dummy so that we have at least one dirent
    32  	dummy, err := locateDummyIfEmpty(path)
    33  	if err != nil {
    34  		return false, err
    35  	}
    36  	if dummy != "" {
    37  		defer os.Remove(dummy)
    38  	}
    39  
    40  	visited := 0
    41  	supportsDType := true
    42  	fn := func(ent *unix.Dirent) bool {
    43  		visited++
    44  		if ent.Type == unix.DT_UNKNOWN {
    45  			supportsDType = false
    46  			// stop iteration
    47  			return true
    48  		}
    49  		// continue iteration
    50  		return false
    51  	}
    52  	if err = iterateReadDir(path, fn); err != nil {
    53  		return false, err
    54  	}
    55  	if visited == 0 {
    56  		return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
    57  	}
    58  	return supportsDType, nil
    59  }
    60  
    61  func iterateReadDir(path string, fn func(*unix.Dirent) bool) error {
    62  	d, err := os.Open(path)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	defer d.Close()
    67  	fd := int(d.Fd())
    68  	buf := make([]byte, 4096)
    69  	for {
    70  		nbytes, err := unix.ReadDirent(fd, buf)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		if nbytes == 0 {
    75  			break
    76  		}
    77  		for off := 0; off < nbytes; {
    78  			ent := (*unix.Dirent)(unsafe.Pointer(&buf[off]))
    79  			if stop := fn(ent); stop {
    80  				return nil
    81  			}
    82  			off += int(ent.Reclen)
    83  		}
    84  	}
    85  	return nil
    86  }