gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/imports/fastwalk_unix.go (about) 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 // +build linux darwin freebsd openbsd netbsd 6 // +build !appengine 7 8 package imports 9 10 import ( 11 "bytes" 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 := syscall.Open(dirName, 0, 0) 26 if err != nil { 27 return 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 for { 36 if bufp >= nbuf { 37 bufp = 0 38 nbuf, err = syscall.ReadDirent(fd, buf) 39 if err != nil { 40 return os.NewSyscallError("readdirent", err) 41 } 42 if nbuf <= 0 { 43 return nil 44 } 45 } 46 consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) 47 bufp += consumed 48 if name == "" || name == "." || name == ".." { 49 continue 50 } 51 // Fallback for filesystems (like old XFS) that don't 52 // support Dirent.Type and have DT_UNKNOWN (0) there 53 // instead. 54 if typ == unknownFileMode { 55 fi, err := os.Lstat(dirName + "/" + name) 56 if err != nil { 57 // It got deleted in the meantime. 58 if os.IsNotExist(err) { 59 continue 60 } 61 return err 62 } 63 typ = fi.Mode() & os.ModeType 64 } 65 if err := fn(dirName, name, typ); err != nil { 66 return err 67 } 68 } 69 } 70 71 func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { 72 // golang.org/issue/15653 73 dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0])) 74 if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { 75 panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) 76 } 77 if len(buf) < int(dirent.Reclen) { 78 panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) 79 } 80 consumed = int(dirent.Reclen) 81 if direntInode(dirent) == 0 { // File absent in directory. 82 return 83 } 84 switch dirent.Type { 85 case syscall.DT_REG: 86 typ = 0 87 case syscall.DT_DIR: 88 typ = os.ModeDir 89 case syscall.DT_LNK: 90 typ = os.ModeSymlink 91 case syscall.DT_BLK: 92 typ = os.ModeDevice 93 case syscall.DT_FIFO: 94 typ = os.ModeNamedPipe 95 case syscall.DT_SOCK: 96 typ = os.ModeSocket 97 case syscall.DT_UNKNOWN: 98 typ = unknownFileMode 99 default: 100 // Skip weird things. 101 // It's probably a DT_WHT (http://lwn.net/Articles/325369/) 102 // or something. Revisit if/when this package is moved outside 103 // of goimports. goimports only cares about regular files, 104 // symlinks, and directories. 105 return 106 } 107 108 nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) 109 nameLen := bytes.IndexByte(nameBuf[:], 0) 110 if nameLen < 0 { 111 panic("failed to find terminating 0 byte in dirent") 112 } 113 114 // Special cases for common things: 115 if nameLen == 1 && nameBuf[0] == '.' { 116 name = "." 117 } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { 118 name = ".." 119 } else { 120 name = string(nameBuf[:nameLen]) 121 } 122 return 123 }