github.com/cdoern/storage@v1.12.13/pkg/fsutils/fsutils_linux.go (about)

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