gopkg.in/docker/docker.v20@v20.10.27/pkg/fsutils/fsutils_linux.go (about)

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