github.com/m3db/m3@v1.5.0/src/x/process/count_dirent_linux.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  // This package is mostly copy-pasted from the standard library, specifically
    22  // this file: https://golang.org/src/os/dir_unix.go with some changes to prevent
    23  // allocations.
    24  
    25  package process
    26  
    27  import (
    28  	"bytes"
    29  	"syscall"
    30  	"unsafe"
    31  )
    32  
    33  func countDirent(buf []byte) (consumed int, count int) {
    34  	origlen := len(buf)
    35  	count = 0
    36  	for len(buf) > 0 {
    37  		reclen, ok := direntReclen(buf)
    38  		if !ok || reclen > uint64(len(buf)) {
    39  			return origlen, count
    40  		}
    41  
    42  		rec := buf[:reclen]
    43  		buf = buf[reclen:]
    44  
    45  		ino, ok := direntIno(rec)
    46  		if !ok {
    47  			break
    48  		}
    49  		if ino == 0 { // File absent in directory.
    50  			continue
    51  		}
    52  		const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
    53  		namlen, ok := direntNamlen(rec)
    54  		if !ok || namoff+namlen > uint64(len(rec)) {
    55  			break
    56  		}
    57  		name := rec[namoff : namoff+namlen]
    58  		for i, c := range name {
    59  			if c == 0 {
    60  				name = name[:i]
    61  				break
    62  			}
    63  		}
    64  
    65  		if bytes.Equal(name, dotBytes) || bytes.Equal(name, doubleDotBytes) {
    66  			// Check for useless names before allocating a string.
    67  			continue
    68  		}
    69  		count++
    70  	}
    71  
    72  	return origlen - len(buf), count
    73  }
    74  
    75  func direntReclen(buf []byte) (uint64, bool) {
    76  	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
    77  }
    78  
    79  func direntIno(buf []byte) (uint64, bool) {
    80  	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
    81  }
    82  
    83  func direntNamlen(buf []byte) (uint64, bool) {
    84  	reclen, ok := direntReclen(buf)
    85  	if !ok {
    86  		return 0, false
    87  	}
    88  	return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
    89  }
    90  
    91  // readInt returns the size-bytes unsigned integer in native byte order at offset off.
    92  func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
    93  	if len(b) < int(off+size) {
    94  		return 0, false
    95  	}
    96  	return readIntLE(b[off:], size), true
    97  }
    98  
    99  func readIntLE(b []byte, size uintptr) uint64 {
   100  	switch size {
   101  	case 1:
   102  		return uint64(b[0])
   103  	case 2:
   104  		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
   105  		return uint64(b[0]) | uint64(b[1])<<8
   106  	case 4:
   107  		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
   108  		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
   109  	case 8:
   110  		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
   111  		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
   112  			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
   113  	default:
   114  		panic("syscall: readInt with unsupported size")
   115  	}
   116  }