github.com/ph/moby@v1.13.1/pkg/fsutils/fsutils_linux.go (about)

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