code.gitea.io/gitea@v1.19.3/modules/packages/nuget/symbol_extractor.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package nuget 5 6 import ( 7 "archive/zip" 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "io" 12 "path" 13 "path/filepath" 14 "strings" 15 16 "code.gitea.io/gitea/modules/packages" 17 "code.gitea.io/gitea/modules/util" 18 ) 19 20 var ( 21 ErrMissingPdbFiles = util.NewInvalidArgumentErrorf("package does not contain PDB files") 22 ErrInvalidFiles = util.NewInvalidArgumentErrorf("package contains invalid files") 23 ErrInvalidPdbMagicNumber = util.NewInvalidArgumentErrorf("invalid Portable PDB magic number") 24 ErrMissingPdbStream = util.NewInvalidArgumentErrorf("missing PDB stream") 25 ) 26 27 type PortablePdb struct { 28 Name string 29 ID string 30 Content *packages.HashedBuffer 31 } 32 33 type PortablePdbList []*PortablePdb 34 35 func (l PortablePdbList) Close() { 36 for _, pdb := range l { 37 pdb.Content.Close() 38 } 39 } 40 41 // ExtractPortablePdb extracts PDB files from a .snupkg file 42 func ExtractPortablePdb(r io.ReaderAt, size int64) (PortablePdbList, error) { 43 archive, err := zip.NewReader(r, size) 44 if err != nil { 45 return nil, err 46 } 47 48 var pdbs PortablePdbList 49 50 err = func() error { 51 for _, file := range archive.File { 52 if strings.HasSuffix(file.Name, "/") { 53 continue 54 } 55 ext := strings.ToLower(filepath.Ext(file.Name)) 56 57 switch ext { 58 case ".nuspec", ".xml", ".psmdcp", ".rels", ".p7s": 59 continue 60 case ".pdb": 61 f, err := archive.Open(file.Name) 62 if err != nil { 63 return err 64 } 65 66 buf, err := packages.CreateHashedBufferFromReader(f, 32*1024*1024) 67 68 f.Close() 69 70 if err != nil { 71 return err 72 } 73 74 id, err := ParseDebugHeaderID(buf) 75 if err != nil { 76 buf.Close() 77 return fmt.Errorf("Invalid PDB file: %w", err) 78 } 79 80 if _, err := buf.Seek(0, io.SeekStart); err != nil { 81 buf.Close() 82 return err 83 } 84 85 pdbs = append(pdbs, &PortablePdb{ 86 Name: path.Base(file.Name), 87 ID: id, 88 Content: buf, 89 }) 90 default: 91 return ErrInvalidFiles 92 } 93 } 94 return nil 95 }() 96 if err != nil { 97 pdbs.Close() 98 return nil, err 99 } 100 101 if len(pdbs) == 0 { 102 return nil, ErrMissingPdbFiles 103 } 104 105 return pdbs, nil 106 } 107 108 // ParseDebugHeaderID TODO 109 func ParseDebugHeaderID(r io.ReadSeeker) (string, error) { 110 var magic uint32 111 if err := binary.Read(r, binary.LittleEndian, &magic); err != nil { 112 return "", err 113 } 114 if magic != 0x424A5342 { 115 return "", ErrInvalidPdbMagicNumber 116 } 117 118 if _, err := r.Seek(8, io.SeekCurrent); err != nil { 119 return "", err 120 } 121 122 var versionStringSize int32 123 if err := binary.Read(r, binary.LittleEndian, &versionStringSize); err != nil { 124 return "", err 125 } 126 if _, err := r.Seek(int64(versionStringSize), io.SeekCurrent); err != nil { 127 return "", err 128 } 129 if _, err := r.Seek(2, io.SeekCurrent); err != nil { 130 return "", err 131 } 132 133 var streamCount int16 134 if err := binary.Read(r, binary.LittleEndian, &streamCount); err != nil { 135 return "", err 136 } 137 138 read4ByteAlignedString := func(r io.Reader) (string, error) { 139 b := make([]byte, 4) 140 var buf bytes.Buffer 141 for { 142 if _, err := r.Read(b); err != nil { 143 return "", err 144 } 145 if i := bytes.IndexByte(b, 0); i != -1 { 146 buf.Write(b[:i]) 147 return buf.String(), nil 148 } 149 buf.Write(b) 150 } 151 } 152 153 for i := 0; i < int(streamCount); i++ { 154 var offset uint32 155 if err := binary.Read(r, binary.LittleEndian, &offset); err != nil { 156 return "", err 157 } 158 if _, err := r.Seek(4, io.SeekCurrent); err != nil { 159 return "", err 160 } 161 name, err := read4ByteAlignedString(r) 162 if err != nil { 163 return "", err 164 } 165 166 if name == "#Pdb" { 167 if _, err := r.Seek(int64(offset), io.SeekStart); err != nil { 168 return "", err 169 } 170 171 b := make([]byte, 16) 172 if _, err := r.Read(b); err != nil { 173 return "", err 174 } 175 176 data1 := binary.LittleEndian.Uint32(b[0:4]) 177 data2 := binary.LittleEndian.Uint16(b[4:6]) 178 data3 := binary.LittleEndian.Uint16(b[6:8]) 179 data4 := b[8:16] 180 181 return fmt.Sprintf("%08x%04x%04x%04x%012x", data1, data2, data3, data4[:2], data4[2:]), nil 182 } 183 } 184 185 return "", ErrMissingPdbStream 186 }