github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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  	err = dummyFile.Close()
    27  	return name, err
    28  }
    29  
    30  // SupportsDType returns whether the filesystem mounted on path supports d_type
    31  func SupportsDType(path string) (bool, error) {
    32  	// locate dummy so that we have at least one dirent
    33  	dummy, err := locateDummyIfEmpty(path)
    34  	if err != nil {
    35  		return false, err
    36  	}
    37  	if dummy != "" {
    38  		defer os.Remove(dummy)
    39  	}
    40  
    41  	visited := 0
    42  	supportsDType := true
    43  	fn := func(ent *syscall.Dirent) bool {
    44  		visited++
    45  		if ent.Type == syscall.DT_UNKNOWN {
    46  			supportsDType = false
    47  			// stop iteration
    48  			return true
    49  		}
    50  		// continue iteration
    51  		return false
    52  	}
    53  	if err = iterateReadDir(path, fn); err != nil {
    54  		return false, err
    55  	}
    56  	if visited == 0 {
    57  		return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
    58  	}
    59  	return supportsDType, nil
    60  }
    61  
    62  func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
    63  	d, err := os.Open(path)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	defer d.Close()
    68  	fd := int(d.Fd())
    69  	buf := make([]byte, 4096)
    70  	for {
    71  		nbytes, err := syscall.ReadDirent(fd, buf)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		if nbytes == 0 {
    76  			break
    77  		}
    78  		for off := 0; off < nbytes; {
    79  			ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
    80  			if stop := fn(ent); stop {
    81  				return nil
    82  			}
    83  			off += int(ent.Reclen)
    84  		}
    85  	}
    86  	return nil
    87  }