You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

155 regels
4.4 KiB

  1. // Copyright (c) 2017, Google Inc.
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14. // ar.go contains functions for parsing .a archive files.
  15. package ar
  16. import (
  17. "bytes"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "strconv"
  22. "strings"
  23. )
  24. // ParseAR parses an archive file from r and returns a map from filename to
  25. // contents, or else an error.
  26. func ParseAR(r io.Reader) (map[string][]byte, error) {
  27. // See https://en.wikipedia.org/wiki/Ar_(Unix)#File_format_details
  28. const expectedMagic = "!<arch>\n"
  29. var magic [len(expectedMagic)]byte
  30. if _, err := io.ReadFull(r, magic[:]); err != nil {
  31. return nil, err
  32. }
  33. if string(magic[:]) != expectedMagic {
  34. return nil, errors.New("ar: not an archive file")
  35. }
  36. const filenameTableName = "//"
  37. const symbolTableName = "/"
  38. var longFilenameTable []byte
  39. ret := make(map[string][]byte)
  40. for {
  41. var header [60]byte
  42. if _, err := io.ReadFull(r, header[:]); err != nil {
  43. if err == io.EOF {
  44. break
  45. }
  46. return nil, errors.New("ar: error reading file header: " + err.Error())
  47. }
  48. name := strings.TrimRight(string(header[:16]), " ")
  49. sizeStr := strings.TrimRight(string(header[48:58]), "\x00 ")
  50. size, err := strconv.ParseUint(sizeStr, 10, 64)
  51. if err != nil {
  52. return nil, errors.New("ar: failed to parse file size: " + err.Error())
  53. }
  54. // File contents are padded to a multiple of two bytes
  55. storedSize := size
  56. if storedSize%2 == 1 {
  57. storedSize++
  58. }
  59. contents := make([]byte, storedSize)
  60. if _, err := io.ReadFull(r, contents); err != nil {
  61. return nil, errors.New("ar: error reading file contents: " + err.Error())
  62. }
  63. contents = contents[:size]
  64. switch {
  65. case name == filenameTableName:
  66. if longFilenameTable != nil {
  67. return nil, errors.New("ar: two filename tables found")
  68. }
  69. longFilenameTable = contents
  70. continue
  71. case name == symbolTableName:
  72. continue
  73. case len(name) > 1 && name[0] == '/':
  74. if longFilenameTable == nil {
  75. return nil, errors.New("ar: long filename reference found before filename table")
  76. }
  77. // A long filename is stored as "/" followed by a
  78. // base-10 offset in the filename table.
  79. offset, err := strconv.ParseUint(name[1:], 10, 64)
  80. if err != nil {
  81. return nil, errors.New("ar: failed to parse filename offset: " + err.Error())
  82. }
  83. if offset > uint64((^uint(0))>>1) {
  84. return nil, errors.New("ar: filename offset overflow")
  85. }
  86. if int(offset) > len(longFilenameTable) {
  87. return nil, errors.New("ar: filename offset out of bounds")
  88. }
  89. filename := longFilenameTable[offset:]
  90. // Windows terminates filenames with NUL characters,
  91. // while sysv/GNU uses /.
  92. if i := bytes.IndexAny(filename, "/\x00"); i < 0 {
  93. return nil, errors.New("ar: unterminated filename in table")
  94. } else {
  95. filename = filename[:i]
  96. }
  97. name = string(filename)
  98. default:
  99. name = strings.TrimRight(name, "/")
  100. }
  101. // Post-processing for BSD:
  102. // https://en.wikipedia.org/wiki/Ar_(Unix)#BSD_variant
  103. //
  104. // If the name is of the form #1/XXX, XXX identifies the length of the
  105. // name, and the name itself is stored as a prefix of the data, possibly
  106. // null-padded.
  107. var namelen uint
  108. n, err := fmt.Sscanf(name, "#1/%d", &namelen)
  109. if err == nil && n == 1 && len(contents) >= int(namelen) {
  110. name = string(contents[:namelen])
  111. contents = contents[namelen:]
  112. // Names can be null padded; find the first null (if any). Note that
  113. // this also handles the case of a null followed by non-null
  114. // characters. It's not clear whether those can ever show up in
  115. // practice, but we might as well handle them in case they can show
  116. // up.
  117. var null int
  118. for ; null < len(name); null++ {
  119. if name[null] == 0 {
  120. break
  121. }
  122. }
  123. name = name[:null]
  124. }
  125. if name == "__.SYMDEF" || name == "__.SYMDEF SORTED" {
  126. continue
  127. }
  128. ret[name] = contents
  129. }
  130. return ret, nil
  131. }