1'use strict' 2const { Minipass } = require('minipass') 3const normPath = require('./normalize-windows-path.js') 4 5const SLURP = Symbol('slurp') 6module.exports = class ReadEntry extends Minipass { 7 constructor (header, ex, gex) { 8 super() 9 // read entries always start life paused. this is to avoid the 10 // situation where Minipass's auto-ending empty streams results 11 // in an entry ending before we're ready for it. 12 this.pause() 13 this.extended = ex 14 this.globalExtended = gex 15 this.header = header 16 this.startBlockSize = 512 * Math.ceil(header.size / 512) 17 this.blockRemain = this.startBlockSize 18 this.remain = header.size 19 this.type = header.type 20 this.meta = false 21 this.ignore = false 22 switch (this.type) { 23 case 'File': 24 case 'OldFile': 25 case 'Link': 26 case 'SymbolicLink': 27 case 'CharacterDevice': 28 case 'BlockDevice': 29 case 'Directory': 30 case 'FIFO': 31 case 'ContiguousFile': 32 case 'GNUDumpDir': 33 break 34 35 case 'NextFileHasLongLinkpath': 36 case 'NextFileHasLongPath': 37 case 'OldGnuLongPath': 38 case 'GlobalExtendedHeader': 39 case 'ExtendedHeader': 40 case 'OldExtendedHeader': 41 this.meta = true 42 break 43 44 // NOTE: gnutar and bsdtar treat unrecognized types as 'File' 45 // it may be worth doing the same, but with a warning. 46 default: 47 this.ignore = true 48 } 49 50 this.path = normPath(header.path) 51 this.mode = header.mode 52 if (this.mode) { 53 this.mode = this.mode & 0o7777 54 } 55 this.uid = header.uid 56 this.gid = header.gid 57 this.uname = header.uname 58 this.gname = header.gname 59 this.size = header.size 60 this.mtime = header.mtime 61 this.atime = header.atime 62 this.ctime = header.ctime 63 this.linkpath = normPath(header.linkpath) 64 this.uname = header.uname 65 this.gname = header.gname 66 67 if (ex) { 68 this[SLURP](ex) 69 } 70 if (gex) { 71 this[SLURP](gex, true) 72 } 73 } 74 75 write (data) { 76 const writeLen = data.length 77 if (writeLen > this.blockRemain) { 78 throw new Error('writing more to entry than is appropriate') 79 } 80 81 const r = this.remain 82 const br = this.blockRemain 83 this.remain = Math.max(0, r - writeLen) 84 this.blockRemain = Math.max(0, br - writeLen) 85 if (this.ignore) { 86 return true 87 } 88 89 if (r >= writeLen) { 90 return super.write(data) 91 } 92 93 // r < writeLen 94 return super.write(data.slice(0, r)) 95 } 96 97 [SLURP] (ex, global) { 98 for (const k in ex) { 99 // we slurp in everything except for the path attribute in 100 // a global extended header, because that's weird. 101 if (ex[k] !== null && ex[k] !== undefined && 102 !(global && k === 'path')) { 103 this[k] = k === 'path' || k === 'linkpath' ? normPath(ex[k]) : ex[k] 104 } 105 } 106 } 107} 108