github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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 }