.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / fastwalk / fastwalk_unix.go
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build (linux || darwin || freebsd || openbsd || netbsd) && !appengine
6 // +build linux darwin freebsd openbsd netbsd
7 // +build !appengine
8
9 package fastwalk
10
11 import (
12         "fmt"
13         "os"
14         "syscall"
15         "unsafe"
16 )
17
18 const blockSize = 8 << 10
19
20 // unknownFileMode is a sentinel (and bogus) os.FileMode
21 // value used to represent a syscall.DT_UNKNOWN Dirent.Type.
22 const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
23
24 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
25         fd, err := open(dirName, 0, 0)
26         if err != nil {
27                 return &os.PathError{Op: "open", Path: dirName, Err: err}
28         }
29         defer syscall.Close(fd)
30
31         // The buffer must be at least a block long.
32         buf := make([]byte, blockSize) // stack-allocated; doesn't escape
33         bufp := 0                      // starting read position in buf
34         nbuf := 0                      // end valid data in buf
35         skipFiles := false
36         for {
37                 if bufp >= nbuf {
38                         bufp = 0
39                         nbuf, err = readDirent(fd, buf)
40                         if err != nil {
41                                 return os.NewSyscallError("readdirent", err)
42                         }
43                         if nbuf <= 0 {
44                                 return nil
45                         }
46                 }
47                 consumed, name, typ := parseDirEnt(buf[bufp:nbuf])
48                 bufp += consumed
49                 if name == "" || name == "." || name == ".." {
50                         continue
51                 }
52                 // Fallback for filesystems (like old XFS) that don't
53                 // support Dirent.Type and have DT_UNKNOWN (0) there
54                 // instead.
55                 if typ == unknownFileMode {
56                         fi, err := os.Lstat(dirName + "/" + name)
57                         if err != nil {
58                                 // It got deleted in the meantime.
59                                 if os.IsNotExist(err) {
60                                         continue
61                                 }
62                                 return err
63                         }
64                         typ = fi.Mode() & os.ModeType
65                 }
66                 if skipFiles && typ.IsRegular() {
67                         continue
68                 }
69                 if err := fn(dirName, name, typ); err != nil {
70                         if err == ErrSkipFiles {
71                                 skipFiles = true
72                                 continue
73                         }
74                         return err
75                 }
76         }
77 }
78
79 func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
80         // golang.org/issue/37269
81         dirent := &syscall.Dirent{}
82         copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf)
83         if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
84                 panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
85         }
86         if len(buf) < int(dirent.Reclen) {
87                 panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen))
88         }
89         consumed = int(dirent.Reclen)
90         if direntInode(dirent) == 0 { // File absent in directory.
91                 return
92         }
93         switch dirent.Type {
94         case syscall.DT_REG:
95                 typ = 0
96         case syscall.DT_DIR:
97                 typ = os.ModeDir
98         case syscall.DT_LNK:
99                 typ = os.ModeSymlink
100         case syscall.DT_BLK:
101                 typ = os.ModeDevice
102         case syscall.DT_FIFO:
103                 typ = os.ModeNamedPipe
104         case syscall.DT_SOCK:
105                 typ = os.ModeSocket
106         case syscall.DT_UNKNOWN:
107                 typ = unknownFileMode
108         default:
109                 // Skip weird things.
110                 // It's probably a DT_WHT (http://lwn.net/Articles/325369/)
111                 // or something. Revisit if/when this package is moved outside
112                 // of goimports. goimports only cares about regular files,
113                 // symlinks, and directories.
114                 return
115         }
116
117         nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
118         nameLen := direntNamlen(dirent)
119
120         // Special cases for common things:
121         if nameLen == 1 && nameBuf[0] == '.' {
122                 name = "."
123         } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' {
124                 name = ".."
125         } else {
126                 name = string(nameBuf[:nameLen])
127         }
128         return
129 }
130
131 // According to https://golang.org/doc/go1.14#runtime
132 // A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS
133 // systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases.
134 //
135 // This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors.
136 // We need to retry in this case.
137 func open(path string, mode int, perm uint32) (fd int, err error) {
138         for {
139                 fd, err := syscall.Open(path, mode, perm)
140                 if err != syscall.EINTR {
141                         return fd, err
142                 }
143         }
144 }
145
146 func readDirent(fd int, buf []byte) (n int, err error) {
147         for {
148                 nbuf, err := syscall.ReadDirent(fd, buf)
149                 if err != syscall.EINTR {
150                         return nbuf, err
151                 }
152         }
153 }