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  }