Add lint and go generate steps to CI (#254)

* Add lint and go generate stages to CI

Add CI step to verify `go generate` was run on repo.
Add linter stage to CI along with linter config file,
`.golangci.yml`.
Will likely prefer revive over static-check.

Updated README Contributing section on linting requirements.

Added sequence ordering to make sure lint and go generate stages run
before tests and build.
This way, build and tests are not run on code that could potentially:

    1. not build due to `gofmt` issues;
    2. contain bugs;
    3. have to be re-submitted after issues are fixed; or
    4. contain outdated Win32 syscall or other auto-generated files.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

* Fixed linter issues

Code changes to satisfy linters:

 - Ran `gofmt -s -w` on repo.
 - Broke up long lines.
 - When possible, changed names with incorrect initialism formatting
   - Added exceptions for exported variables.
 - Added exceptions for ALL_CAPS_WITH_UNDERSCORES code.
   - Switched to using `windows` or `syscall` definitions if possible;
     especially if some constants were unused.
 - Added `_ =` to satisfy error linter, and acknowledge that errors are
   being ignored.
 - Switched to using `errors.Is` and `As` in places, elsewhere added
   exceptions if error value was known to be `syscall.Errno`.
 - Removed bare returns.
 - Prevented variables from being overshadowed in certain places
   (ignoring cases of overshadowing `err`).
 - Renamed variables and functions (eg, `len`, `eventMetadata.bytes`) to
   prevent shadowing pre-built functions and imported pacakges.
 - Removed unused method receivers.
 - Added exceptions to certain unused (unexported) constants and
   functions.
   - Deleted unused `once` from `pkg/etw.providerMap`.
 - Renamed `noop.go` files to `main_other.go` or `doc.go`, to better fit
   style recommendations.
 - Added exceptions for non-secure use of SHA1 and weak crypto
   libraries.
 - Replaced `ioutil` with `io` and `os` (and `t.TempDir` in tests).
 - Added fully exhaustive checks for `switch` statements in `pkg/etw`.
 - Defined constant strings for `tools/mkwinsyscall`.
 - Removed unnecessary conversions.
 - Made sure `context.Cancel` was called.

Additionally, added `//go:build windows" constraints on files with
unexported code, since linter will complain about unused code on
non-Windows platforms.

Added a stub `main() {}` for `mkwinsyscall` for non-Windows builds, just in
case `//go:generate` directives are added to OS-agnostic files.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

* PR: spelling, constants, fuzzing

Moved HVSocket fuzzing tests to separate file with go 1.18 build
constraint.

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
This commit is contained in:
Hamza El-Saawy
2022-08-23 15:05:05 -04:00
committed by GitHub
parent 79ae8cea02
commit e268c11e27
67 changed files with 1192 additions and 674 deletions
+4 -5
View File
@@ -1,3 +1,4 @@
//go:build windows || linux
// +build windows linux
package wim
@@ -5,7 +6,6 @@ package wim
import (
"encoding/binary"
"io"
"io/ioutil"
"github.com/Microsoft/go-winio/wim/lzx"
)
@@ -35,7 +35,6 @@ func newCompressedReader(r *io.SectionReader, originalSize int64, offset int64)
for i, n := range chunks32 {
chunks[i+1] = int64(n)
}
} else {
// 64-bit chunk offsets
base = (nchunks - 1) * 8
@@ -62,7 +61,7 @@ func newCompressedReader(r *io.SectionReader, originalSize int64, offset int64)
suboff := offset % chunkSize
if suboff != 0 {
_, err := io.CopyN(ioutil.Discard, cr.d, suboff)
_, err := io.CopyN(io.Discard, cr.d, suboff)
if err != nil {
return nil, err
}
@@ -110,7 +109,7 @@ func (r *compressedReader) reset(n int) error {
}
r.d = d
} else {
r.d = ioutil.NopCloser(section)
r.d = io.NopCloser(section)
}
return nil
@@ -119,7 +118,7 @@ func (r *compressedReader) reset(n int) error {
func (r *compressedReader) Read(b []byte) (int, error) {
for {
n, err := r.d.Read(b)
if err != io.EOF {
if err != io.EOF { //nolint:errorlint
return n, err
}
+14 -21
View File
@@ -100,7 +100,7 @@ func (f *decompressor) ensureAtLeast(n int) error {
}
n, err := io.ReadAtLeast(f.r, f.b[f.bv-f.bo:], n)
if err != nil {
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
err = io.ErrUnexpectedEOF
} else {
f.fail(err)
@@ -117,10 +117,8 @@ func (f *decompressor) ensureAtLeast(n int) error {
// Otherwise, on error, it sets f.err.
func (f *decompressor) feed() bool {
err := f.ensureAtLeast(2)
if err != nil {
if err == io.ErrUnexpectedEOF {
return false
}
if err == io.ErrUnexpectedEOF { //nolint:errorlint // returns io.ErrUnexpectedEOF by contract
return false
}
f.c |= (uint32(f.b[f.bo+1])<<8 | uint32(f.b[f.bo])) << (16 - f.nbits)
f.nbits += 16
@@ -232,9 +230,8 @@ func (f *decompressor) getCode(h *huffman) uint16 {
// are, since entries with all possible suffixes were
// added to the table.
c := h.table[f.c>>(32-tablebits)]
if c >= 1<<lenshift {
// The code is already in c.
} else {
if !(c >= 1<<lenshift) {
// The code is not in c.
c = h.extra[c][f.c<<tablebits>>(32-(h.maxbits-tablebits))]
}
@@ -399,41 +396,37 @@ func (f *decompressor) readTrees(readAligned bool) (main *huffman, length *huffm
}
aligned = buildTable(alignedLen[:])
if aligned == nil {
err = errors.New("corrupt")
return
return main, length, aligned, errors.New("corrupt")
}
}
// The main tree is encoded in two parts.
err = f.readTree(f.mainlens[:maincodesplit])
if err != nil {
return
return main, length, aligned, err
}
err = f.readTree(f.mainlens[maincodesplit:])
if err != nil {
return
return main, length, aligned, err
}
main = buildTable(f.mainlens[:])
if main == nil {
err = errors.New("corrupt")
return
return main, length, aligned, errors.New("corrupt")
}
// The length tree is encoding in a single part.
err = f.readTree(f.lenlens[:])
if err != nil {
return
return main, length, aligned, err
}
length = buildTable(f.lenlens[:])
if length == nil {
err = errors.New("corrupt")
return
return main, length, aligned, errors.New("corrupt")
}
err = f.err
return
return main, length, aligned, f.err
}
// readCompressedBlock decodes a compressed block, writing into the window
@@ -465,7 +458,7 @@ func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, ha
matchlen += 2
var matchoffset uint16
if slot < 3 {
if slot < 3 { //nolint:nestif // todo: simplify nested complexity
// The offset is one of the LRU values.
matchoffset = f.lru[slot]
f.lru[slot] = f.lru[0]
@@ -586,7 +579,7 @@ func (f *decompressor) Read(b []byte) (int, error) {
return f.windowReader.Read(b)
}
func (f *decompressor) Close() error {
func (*decompressor) Close() error {
return nil
}
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package main
@@ -1,3 +1,4 @@
//go:build windows
// +build windows
package main
@@ -20,7 +21,6 @@ func main() {
w, err := wim.NewReader(f)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n%#v\n", w.Image[0], w.Image[0].Windows)
@@ -39,13 +39,13 @@ func main() {
func recur(d *wim.File) error {
files, err := d.Readdir()
if err != nil {
return fmt.Errorf("%s: %s", d.Name, err)
return fmt.Errorf("%s: %w", d.Name, err)
}
for _, f := range files {
if f.IsDir() {
err = recur(f)
if err != nil {
return fmt.Errorf("%s: %s", f.Name, err)
return fmt.Errorf("%s: %w", f.Name, err)
}
}
}
+71 -41
View File
@@ -1,3 +1,4 @@
//go:build windows || linux
// +build windows linux
// Package wim implements a WIM file parser.
@@ -8,13 +9,12 @@ package wim
import (
"bytes"
"crypto/sha1"
"crypto/sha1" //nolint:gosec // not used for secure application
"encoding/binary"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"strconv"
"sync"
"time"
@@ -22,6 +22,8 @@ import (
)
// File attribute constants from Windows.
//
//nolint:revive // var-naming: ALL_CAPS
const (
FILE_ATTRIBUTE_READONLY = 0x00000001
FILE_ATTRIBUTE_HIDDEN = 0x00000002
@@ -44,6 +46,8 @@ const (
)
// Windows processor architectures.
//
//nolint:revive // var-naming: ALL_CAPS
const (
PROCESSOR_ARCHITECTURE_INTEL = 0
PROCESSOR_ARCHITECTURE_MIPS = 1
@@ -62,6 +66,8 @@ const (
var wimImageTag = [...]byte{'M', 'S', 'W', 'I', 'M', 0, 0, 0}
// todo: replace this with pkg/guid.GUID (and add tests to make sure nothing breaks)
type guid struct {
Data1 uint32
Data2 uint16
@@ -70,7 +76,18 @@ type guid struct {
}
func (g guid) String() string {
return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", g.Data1, g.Data2, g.Data3, g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7])
return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
g.Data1,
g.Data2,
g.Data3,
g.Data4[0],
g.Data4[1],
g.Data4[2],
g.Data4[3],
g.Data4[4],
g.Data4[5],
g.Data4[6],
g.Data4[7])
}
type resourceDescriptor struct {
@@ -81,6 +98,7 @@ type resourceDescriptor struct {
type resFlag byte
//nolint:deadcode,varcheck // need unused variables for iota to work
const (
resFlagFree resFlag = 1 << iota
resFlagMetadata
@@ -120,6 +138,7 @@ type streamDescriptor struct {
type hdrFlag uint32
//nolint:deadcode,varcheck // need unused variables for iota to work
const (
hdrFlagReserved hdrFlag = 1 << iota
hdrFlagCompressed
@@ -131,6 +150,7 @@ const (
hdrFlagRpFix
)
//nolint:deadcode,varcheck // need unused variables for iota to work
const (
hdrFlagCompressReserved hdrFlag = 1 << (iota + 16)
hdrFlagCompressXpress
@@ -208,13 +228,13 @@ func (ft *Filetime) Time() time.Time {
return time.Unix(0, nsec)
}
// UnmarshalXML unmarshals the time from a WIM XML blob.
// UnmarshalXML unmarshalls the time from a WIM XML blob.
func (ft *Filetime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type time struct {
type Time struct {
Low string `xml:"LOWPART"`
High string `xml:"HIGHPART"`
}
var t time
var t Time
err := d.DecodeElement(&t, &start)
if err != nil {
return err
@@ -283,6 +303,8 @@ func (e *ParseError) Error() string {
return fmt.Sprintf("WIM parse error: %s %s: %s", e.Oper, e.Path, e.Err.Error())
}
func (e *ParseError) Unwrap() error { return e.Err }
// Reader provides functions to read a WIM file.
type Reader struct {
hdr wimHeader
@@ -380,14 +402,14 @@ func NewReader(f io.ReaderAt) (*Reader, error) {
return nil, err
}
var info info
err = xml.Unmarshal([]byte(xmlinfo), &info)
var inf info
err = xml.Unmarshal([]byte(xmlinfo), &inf)
if err != nil {
return nil, &ParseError{Oper: "XML info", Err: err}
}
for i, img := range images {
for _, imgInfo := range info.Image {
for _, imgInfo := range inf.Image {
if imgInfo.Index == i+1 {
img.ImageInfo = imgInfo
break
@@ -417,8 +439,8 @@ func (r *Reader) resourceReaderWithOffset(hdr *resourceDescriptor, offset int64)
var sr io.ReadCloser
section := io.NewSectionReader(r.r, hdr.Offset, hdr.CompressedSize())
if hdr.Flags()&resFlagCompressed == 0 {
section.Seek(offset, 0)
sr = ioutil.NopCloser(section)
_, _ = section.Seek(offset, 0)
sr = io.NopCloser(section)
} else {
cr, err := newCompressedReader(section, hdr.OriginalSize, offset)
if err != nil {
@@ -436,7 +458,7 @@ func (r *Reader) readResource(hdr *resourceDescriptor) ([]byte, error) {
return nil, err
}
defer rsrc.Close()
return ioutil.ReadAll(rsrc)
return io.ReadAll(rsrc)
}
func (r *Reader) readXML() (string, error) {
@@ -449,17 +471,17 @@ func (r *Reader) readXML() (string, error) {
}
defer rsrc.Close()
XMLData := make([]uint16, r.hdr.XMLData.OriginalSize/2)
err = binary.Read(rsrc, binary.LittleEndian, XMLData)
xmlData := make([]uint16, r.hdr.XMLData.OriginalSize/2)
err = binary.Read(rsrc, binary.LittleEndian, xmlData)
if err != nil {
return "", &ParseError{Oper: "XML data", Err: err}
}
// The BOM will always indicate little-endian UTF-16.
if XMLData[0] != 0xfeff {
if xmlData[0] != 0xfeff {
return "", &ParseError{Oper: "XML data", Err: errors.New("invalid BOM")}
}
return string(utf16.Decode(XMLData[1:])), nil
return string(utf16.Decode(xmlData[1:])), nil
}
func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resourceDescriptor, []*Image, error) {
@@ -475,7 +497,7 @@ func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resource
for i := 0; ; i++ {
var res streamDescriptor
err := binary.Read(br, binary.LittleEndian, &res)
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
break
}
if err != nil {
@@ -491,7 +513,7 @@ func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resource
if err != nil {
panic(fmt.Sprint(i, err))
}
hash := sha1.New()
hash := sha1.New() //nolint:gosec // not used for secure application
_, err = io.Copy(hash, sec)
sec.Close()
if err != nil {
@@ -522,12 +544,11 @@ func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resource
return fileData, images, nil
}
func (r *Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64, err error) {
func (*Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64, err error) {
var secBlock securityblockDisk
err = binary.Read(rsrc, binary.LittleEndian, &secBlock)
if err != nil {
err = &ParseError{Oper: "security table", Err: err}
return
return sds, 0, &ParseError{Oper: "security table", Err: err}
}
n += securityblockDiskSize
@@ -535,8 +556,7 @@ func (r *Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64,
secSizes := make([]int64, secBlock.NumEntries)
err = binary.Read(rsrc, binary.LittleEndian, &secSizes)
if err != nil {
err = &ParseError{Oper: "security table sizes", Err: err}
return
return sds, n, &ParseError{Oper: "security table sizes", Err: err}
}
n += int64(secBlock.NumEntries * 8)
@@ -546,8 +566,7 @@ func (r *Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64,
sd := make([]byte, size&0xffffffff)
_, err = io.ReadFull(rsrc, sd)
if err != nil {
err = &ParseError{Oper: "security descriptor", Err: err}
return
return sds, n, &ParseError{Oper: "security descriptor", Err: err}
}
n += int64(len(sd))
sds[i] = sd
@@ -555,17 +574,16 @@ func (r *Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64,
secsize := int64((secBlock.TotalLength + 7) &^ 7)
if n > secsize {
err = &ParseError{Oper: "security descriptor", Err: errors.New("security descriptor table too small")}
return
return sds, n, &ParseError{Oper: "security descriptor", Err: errors.New("security descriptor table too small")}
}
_, err = io.CopyN(ioutil.Discard, rsrc, secsize-n)
_, err = io.CopyN(io.Discard, rsrc, secsize-n)
if err != nil {
return
return sds, n, err
}
n = secsize
return
return sds, n, nil
}
// Open parses the image and returns the root directory.
@@ -621,10 +639,10 @@ func (img *Image) readdir(offset int64) ([]*File, error) {
img.curOffset = offset
}
if offset > img.curOffset {
_, err := io.CopyN(ioutil.Discard, img.r, offset-img.curOffset)
_, err := io.CopyN(io.Discard, img.r, offset-img.curOffset)
if err != nil {
img.reset()
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
err = io.ErrUnexpectedEOF
}
return nil, err
@@ -635,7 +653,7 @@ func (img *Image) readdir(offset int64) ([]*File, error) {
for {
e, n, err := img.readNextEntry(img.r)
img.curOffset += n
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
break
}
if err != nil {
@@ -699,7 +717,11 @@ func (img *Image) readNextEntry(r io.Reader) (*File, int64, error) {
var ok bool
offset, ok = img.wim.fileData[dentry.Hash]
if !ok {
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: fmt.Errorf("could not find file data matching hash %#v", dentry)}
return nil, 0, &ParseError{
Oper: "directory entry",
Path: name,
Err: fmt.Errorf("could not find file data matching hash %#v", dentry),
}
}
}
@@ -742,9 +764,9 @@ func (img *Image) readNextEntry(r io.Reader) (*File, int64, error) {
f.SecurityDescriptor = img.sds[dentry.SecurityID]
}
_, err = io.CopyN(ioutil.Discard, r, left)
_, err = io.CopyN(io.Discard, r, left)
if err != nil {
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
err = io.ErrUnexpectedEOF
}
return nil, 0, err
@@ -771,7 +793,11 @@ func (img *Image) readNextEntry(r io.Reader) (*File, int64, error) {
}
if dentry.Attributes&FILE_ATTRIBUTE_REPARSE_POINT != 0 && f.Size == 0 {
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("reparse point is missing reparse stream")}
return nil, 0, &ParseError{
Oper: "directory entry",
Path: name,
Err: errors.New("reparse point is missing reparse stream"),
}
}
return f, length, nil
@@ -781,7 +807,7 @@ func (img *Image) readNextStream(r io.Reader) (*Stream, int64, error) {
var length int64
err := binary.Read(r, binary.LittleEndian, &length)
if err != nil {
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
err = io.ErrUnexpectedEOF
}
return nil, 0, &ParseError{Oper: "stream length check", Err: err}
@@ -818,7 +844,11 @@ func (img *Image) readNextStream(r io.Reader) (*Stream, int64, error) {
var ok bool
offset, ok = img.wim.fileData[sentry.Hash]
if !ok {
return nil, 0, &ParseError{Oper: "stream entry", Path: name, Err: fmt.Errorf("could not find file data matching hash %v", sentry.Hash)}
return nil, 0, &ParseError{
Oper: "stream entry",
Path: name,
Err: fmt.Errorf("could not find file data matching hash %v", sentry.Hash),
}
}
}
@@ -832,9 +862,9 @@ func (img *Image) readNextStream(r io.Reader) (*Stream, int64, error) {
offset: offset,
}
_, err = io.CopyN(ioutil.Discard, r, left)
_, err = io.CopyN(io.Discard, r, left)
if err != nil {
if err == io.EOF {
if err == io.EOF { //nolint:errorlint
err = io.ErrUnexpectedEOF
}
return nil, 0, err