github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/fs/readdircount_linux_amd64.go (about)

     1  // +build linux,amd64
     2  
     3  package fs
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"unsafe"
     9  
    10  	"syscall"
    11  )
    12  
    13  func countDirEntries(buf []byte, n int) int {
    14  	count := 0
    15  	buf = buf[:n]
    16  	for len(buf) > 0 {
    17  		// see man page getdents(2) for struct linux_dirent64
    18  		reclenOffset := unsafe.Offsetof(syscall.Dirent{}.Reclen)
    19  		reclen := *(*uint16)(unsafe.Pointer(&buf[reclenOffset]))
    20  
    21  		inoOffset := unsafe.Offsetof(syscall.Dirent{}.Ino)
    22  		ino := *(*uint64)(unsafe.Pointer(&buf[inoOffset]))
    23  
    24  		if int(reclen) > len(buf) {
    25  			return count
    26  		}
    27  		buf = buf[reclen:]
    28  		if ino == 0 {
    29  			continue
    30  		}
    31  		count++
    32  	}
    33  	return count
    34  }
    35  
    36  // ReadDirCount is similar to ReadDirNames() and then counting with len() but
    37  // it is optimized to avoid parsing the entries
    38  func (realFS) ReadDirCount(dir string) (int, error) {
    39  	buf := make([]byte, 4096)
    40  	fh, err := os.Open(dir)
    41  	if err != nil {
    42  		return 0, err
    43  	}
    44  	defer fh.Close()
    45  
    46  	openFilesCount := 0
    47  	for {
    48  		n, err := syscall.ReadDirent(int(fh.Fd()), buf)
    49  		if err != nil {
    50  			return 0, fmt.Errorf("ReadDirent() failed: %v", err)
    51  		}
    52  		if n == 0 {
    53  			break
    54  		}
    55  
    56  		openFilesCount += countDirEntries(buf, n)
    57  	}
    58  
    59  	// "." and ".." don't count as files to be counted
    60  	nDotFiles := 2
    61  	return openFilesCount - nDotFiles, err
    62  }