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