headers.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. var alloc = Buffer.alloc
  2. var ZEROS = '0000000000000000000'
  3. var SEVENS = '7777777777777777777'
  4. var ZERO_OFFSET = '0'.charCodeAt(0)
  5. var USTAR_MAGIC = Buffer.from('ustar\x00', 'binary')
  6. var USTAR_VER = Buffer.from('00', 'binary')
  7. var GNU_MAGIC = Buffer.from('ustar\x20', 'binary')
  8. var GNU_VER = Buffer.from('\x20\x00', 'binary')
  9. var MASK = parseInt('7777', 8)
  10. var MAGIC_OFFSET = 257
  11. var VERSION_OFFSET = 263
  12. var clamp = function (index, len, defaultValue) {
  13. if (typeof index !== 'number') return defaultValue
  14. index = ~~index // Coerce to integer.
  15. if (index >= len) return len
  16. if (index >= 0) return index
  17. index += len
  18. if (index >= 0) return index
  19. return 0
  20. }
  21. var toType = function (flag) {
  22. switch (flag) {
  23. case 0:
  24. return 'file'
  25. case 1:
  26. return 'link'
  27. case 2:
  28. return 'symlink'
  29. case 3:
  30. return 'character-device'
  31. case 4:
  32. return 'block-device'
  33. case 5:
  34. return 'directory'
  35. case 6:
  36. return 'fifo'
  37. case 7:
  38. return 'contiguous-file'
  39. case 72:
  40. return 'pax-header'
  41. case 55:
  42. return 'pax-global-header'
  43. case 27:
  44. return 'gnu-long-link-path'
  45. case 28:
  46. case 30:
  47. return 'gnu-long-path'
  48. }
  49. return null
  50. }
  51. var toTypeflag = function (flag) {
  52. switch (flag) {
  53. case 'file':
  54. return 0
  55. case 'link':
  56. return 1
  57. case 'symlink':
  58. return 2
  59. case 'character-device':
  60. return 3
  61. case 'block-device':
  62. return 4
  63. case 'directory':
  64. return 5
  65. case 'fifo':
  66. return 6
  67. case 'contiguous-file':
  68. return 7
  69. case 'pax-header':
  70. return 72
  71. }
  72. return 0
  73. }
  74. var indexOf = function (block, num, offset, end) {
  75. for (; offset < end; offset++) {
  76. if (block[offset] === num) return offset
  77. }
  78. return end
  79. }
  80. var cksum = function (block) {
  81. var sum = 8 * 32
  82. for (var i = 0; i < 148; i++) sum += block[i]
  83. for (var j = 156; j < 512; j++) sum += block[j]
  84. return sum
  85. }
  86. var encodeOct = function (val, n) {
  87. val = val.toString(8)
  88. if (val.length > n) return SEVENS.slice(0, n) + ' '
  89. else return ZEROS.slice(0, n - val.length) + val + ' '
  90. }
  91. /* Copied from the node-tar repo and modified to meet
  92. * tar-stream coding standard.
  93. *
  94. * Source: https://github.com/npm/node-tar/blob/51b6627a1f357d2eb433e7378e5f05e83b7aa6cd/lib/header.js#L349
  95. */
  96. function parse256 (buf) {
  97. // first byte MUST be either 80 or FF
  98. // 80 for positive, FF for 2's comp
  99. var positive
  100. if (buf[0] === 0x80) positive = true
  101. else if (buf[0] === 0xFF) positive = false
  102. else return null
  103. // build up a base-256 tuple from the least sig to the highest
  104. var tuple = []
  105. for (var i = buf.length - 1; i > 0; i--) {
  106. var byte = buf[i]
  107. if (positive) tuple.push(byte)
  108. else tuple.push(0xFF - byte)
  109. }
  110. var sum = 0
  111. var l = tuple.length
  112. for (i = 0; i < l; i++) {
  113. sum += tuple[i] * Math.pow(256, i)
  114. }
  115. return positive ? sum : -1 * sum
  116. }
  117. var decodeOct = function (val, offset, length) {
  118. val = val.slice(offset, offset + length)
  119. offset = 0
  120. // If prefixed with 0x80 then parse as a base-256 integer
  121. if (val[offset] & 0x80) {
  122. return parse256(val)
  123. } else {
  124. // Older versions of tar can prefix with spaces
  125. while (offset < val.length && val[offset] === 32) offset++
  126. var end = clamp(indexOf(val, 32, offset, val.length), val.length, val.length)
  127. while (offset < end && val[offset] === 0) offset++
  128. if (end === offset) return 0
  129. return parseInt(val.slice(offset, end).toString(), 8)
  130. }
  131. }
  132. var decodeStr = function (val, offset, length, encoding) {
  133. return val.slice(offset, indexOf(val, 0, offset, offset + length)).toString(encoding)
  134. }
  135. var addLength = function (str) {
  136. var len = Buffer.byteLength(str)
  137. var digits = Math.floor(Math.log(len) / Math.log(10)) + 1
  138. if (len + digits >= Math.pow(10, digits)) digits++
  139. return (len + digits) + str
  140. }
  141. exports.decodeLongPath = function (buf, encoding) {
  142. return decodeStr(buf, 0, buf.length, encoding)
  143. }
  144. exports.encodePax = function (opts) { // TODO: encode more stuff in pax
  145. var result = ''
  146. if (opts.name) result += addLength(' path=' + opts.name + '\n')
  147. if (opts.linkname) result += addLength(' linkpath=' + opts.linkname + '\n')
  148. var pax = opts.pax
  149. if (pax) {
  150. for (var key in pax) {
  151. result += addLength(' ' + key + '=' + pax[key] + '\n')
  152. }
  153. }
  154. return Buffer.from(result)
  155. }
  156. exports.decodePax = function (buf) {
  157. var result = {}
  158. while (buf.length) {
  159. var i = 0
  160. while (i < buf.length && buf[i] !== 32) i++
  161. var len = parseInt(buf.slice(0, i).toString(), 10)
  162. if (!len) return result
  163. var b = buf.slice(i + 1, len - 1).toString()
  164. var keyIndex = b.indexOf('=')
  165. if (keyIndex === -1) return result
  166. result[b.slice(0, keyIndex)] = b.slice(keyIndex + 1)
  167. buf = buf.slice(len)
  168. }
  169. return result
  170. }
  171. exports.encode = function (opts) {
  172. var buf = alloc(512)
  173. var name = opts.name
  174. var prefix = ''
  175. if (opts.typeflag === 5 && name[name.length - 1] !== '/') name += '/'
  176. if (Buffer.byteLength(name) !== name.length) return null // utf-8
  177. while (Buffer.byteLength(name) > 100) {
  178. var i = name.indexOf('/')
  179. if (i === -1) return null
  180. prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i)
  181. name = name.slice(i + 1)
  182. }
  183. if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null
  184. if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null
  185. buf.write(name)
  186. buf.write(encodeOct(opts.mode & MASK, 6), 100)
  187. buf.write(encodeOct(opts.uid, 6), 108)
  188. buf.write(encodeOct(opts.gid, 6), 116)
  189. buf.write(encodeOct(opts.size, 11), 124)
  190. buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136)
  191. buf[156] = ZERO_OFFSET + toTypeflag(opts.type)
  192. if (opts.linkname) buf.write(opts.linkname, 157)
  193. USTAR_MAGIC.copy(buf, MAGIC_OFFSET)
  194. USTAR_VER.copy(buf, VERSION_OFFSET)
  195. if (opts.uname) buf.write(opts.uname, 265)
  196. if (opts.gname) buf.write(opts.gname, 297)
  197. buf.write(encodeOct(opts.devmajor || 0, 6), 329)
  198. buf.write(encodeOct(opts.devminor || 0, 6), 337)
  199. if (prefix) buf.write(prefix, 345)
  200. buf.write(encodeOct(cksum(buf), 6), 148)
  201. return buf
  202. }
  203. exports.decode = function (buf, filenameEncoding, allowUnknownFormat) {
  204. var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET
  205. var name = decodeStr(buf, 0, 100, filenameEncoding)
  206. var mode = decodeOct(buf, 100, 8)
  207. var uid = decodeOct(buf, 108, 8)
  208. var gid = decodeOct(buf, 116, 8)
  209. var size = decodeOct(buf, 124, 12)
  210. var mtime = decodeOct(buf, 136, 12)
  211. var type = toType(typeflag)
  212. var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100, filenameEncoding)
  213. var uname = decodeStr(buf, 265, 32)
  214. var gname = decodeStr(buf, 297, 32)
  215. var devmajor = decodeOct(buf, 329, 8)
  216. var devminor = decodeOct(buf, 337, 8)
  217. var c = cksum(buf)
  218. // checksum is still initial value if header was null.
  219. if (c === 8 * 32) return null
  220. // valid checksum
  221. if (c !== decodeOct(buf, 148, 8)) throw new Error('Invalid tar header. Maybe the tar is corrupted or it needs to be gunzipped?')
  222. if (USTAR_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0) {
  223. // ustar (posix) format.
  224. // prepend prefix, if present.
  225. if (buf[345]) name = decodeStr(buf, 345, 155, filenameEncoding) + '/' + name
  226. } else if (GNU_MAGIC.compare(buf, MAGIC_OFFSET, MAGIC_OFFSET + 6) === 0 &&
  227. GNU_VER.compare(buf, VERSION_OFFSET, VERSION_OFFSET + 2) === 0) {
  228. // 'gnu'/'oldgnu' format. Similar to ustar, but has support for incremental and
  229. // multi-volume tarballs.
  230. } else {
  231. if (!allowUnknownFormat) {
  232. throw new Error('Invalid tar header: unknown format.')
  233. }
  234. }
  235. // to support old tar versions that use trailing / to indicate dirs
  236. if (typeflag === 0 && name && name[name.length - 1] === '/') typeflag = 5
  237. return {
  238. name,
  239. mode,
  240. uid,
  241. gid,
  242. size,
  243. mtime: new Date(1000 * mtime),
  244. type,
  245. linkname,
  246. uname,
  247. gname,
  248. devmajor,
  249. devminor
  250. }
  251. }