source: trip-planner-front/node_modules/tar/lib/header.js@ b738035

Last change on this file since b738035 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 8.8 KB
Line 
1'use strict'
2// parse a 512-byte header block to a data object, or vice-versa
3// encode returns `true` if a pax extended header is needed, because
4// the data could not be faithfully encoded in a simple header.
5// (Also, check header.needPax to see if it needs a pax header.)
6
7const types = require('./types.js')
8const pathModule = require('path').posix
9const large = require('./large-numbers.js')
10
11const SLURP = Symbol('slurp')
12const TYPE = Symbol('type')
13
14class Header {
15 constructor (data, off, ex, gex) {
16 this.cksumValid = false
17 this.needPax = false
18 this.nullBlock = false
19
20 this.block = null
21 this.path = null
22 this.mode = null
23 this.uid = null
24 this.gid = null
25 this.size = null
26 this.mtime = null
27 this.cksum = null
28 this[TYPE] = '0'
29 this.linkpath = null
30 this.uname = null
31 this.gname = null
32 this.devmaj = 0
33 this.devmin = 0
34 this.atime = null
35 this.ctime = null
36
37 if (Buffer.isBuffer(data))
38 this.decode(data, off || 0, ex, gex)
39 else if (data)
40 this.set(data)
41 }
42
43 decode (buf, off, ex, gex) {
44 if (!off)
45 off = 0
46
47 if (!buf || !(buf.length >= off + 512))
48 throw new Error('need 512 bytes for header')
49
50 this.path = decString(buf, off, 100)
51 this.mode = decNumber(buf, off + 100, 8)
52 this.uid = decNumber(buf, off + 108, 8)
53 this.gid = decNumber(buf, off + 116, 8)
54 this.size = decNumber(buf, off + 124, 12)
55 this.mtime = decDate(buf, off + 136, 12)
56 this.cksum = decNumber(buf, off + 148, 12)
57
58 // if we have extended or global extended headers, apply them now
59 // See https://github.com/npm/node-tar/pull/187
60 this[SLURP](ex)
61 this[SLURP](gex, true)
62
63 // old tar versions marked dirs as a file with a trailing /
64 this[TYPE] = decString(buf, off + 156, 1)
65 if (this[TYPE] === '')
66 this[TYPE] = '0'
67 if (this[TYPE] === '0' && this.path.substr(-1) === '/')
68 this[TYPE] = '5'
69
70 // tar implementations sometimes incorrectly put the stat(dir).size
71 // as the size in the tarball, even though Directory entries are
72 // not able to have any body at all. In the very rare chance that
73 // it actually DOES have a body, we weren't going to do anything with
74 // it anyway, and it'll just be a warning about an invalid header.
75 if (this[TYPE] === '5')
76 this.size = 0
77
78 this.linkpath = decString(buf, off + 157, 100)
79 if (buf.slice(off + 257, off + 265).toString() === 'ustar\u000000') {
80 this.uname = decString(buf, off + 265, 32)
81 this.gname = decString(buf, off + 297, 32)
82 this.devmaj = decNumber(buf, off + 329, 8)
83 this.devmin = decNumber(buf, off + 337, 8)
84 if (buf[off + 475] !== 0) {
85 // definitely a prefix, definitely >130 chars.
86 const prefix = decString(buf, off + 345, 155)
87 this.path = prefix + '/' + this.path
88 } else {
89 const prefix = decString(buf, off + 345, 130)
90 if (prefix)
91 this.path = prefix + '/' + this.path
92 this.atime = decDate(buf, off + 476, 12)
93 this.ctime = decDate(buf, off + 488, 12)
94 }
95 }
96
97 let sum = 8 * 0x20
98 for (let i = off; i < off + 148; i++)
99 sum += buf[i]
100
101 for (let i = off + 156; i < off + 512; i++)
102 sum += buf[i]
103
104 this.cksumValid = sum === this.cksum
105 if (this.cksum === null && sum === 8 * 0x20)
106 this.nullBlock = true
107 }
108
109 [SLURP] (ex, global) {
110 for (const k in ex) {
111 // we slurp in everything except for the path attribute in
112 // a global extended header, because that's weird.
113 if (ex[k] !== null && ex[k] !== undefined &&
114 !(global && k === 'path'))
115 this[k] = ex[k]
116 }
117 }
118
119 encode (buf, off) {
120 if (!buf) {
121 buf = this.block = Buffer.alloc(512)
122 off = 0
123 }
124
125 if (!off)
126 off = 0
127
128 if (!(buf.length >= off + 512))
129 throw new Error('need 512 bytes for header')
130
131 const prefixSize = this.ctime || this.atime ? 130 : 155
132 const split = splitPrefix(this.path || '', prefixSize)
133 const path = split[0]
134 const prefix = split[1]
135 this.needPax = split[2]
136
137 this.needPax = encString(buf, off, 100, path) || this.needPax
138 this.needPax = encNumber(buf, off + 100, 8, this.mode) || this.needPax
139 this.needPax = encNumber(buf, off + 108, 8, this.uid) || this.needPax
140 this.needPax = encNumber(buf, off + 116, 8, this.gid) || this.needPax
141 this.needPax = encNumber(buf, off + 124, 12, this.size) || this.needPax
142 this.needPax = encDate(buf, off + 136, 12, this.mtime) || this.needPax
143 buf[off + 156] = this[TYPE].charCodeAt(0)
144 this.needPax = encString(buf, off + 157, 100, this.linkpath) || this.needPax
145 buf.write('ustar\u000000', off + 257, 8)
146 this.needPax = encString(buf, off + 265, 32, this.uname) || this.needPax
147 this.needPax = encString(buf, off + 297, 32, this.gname) || this.needPax
148 this.needPax = encNumber(buf, off + 329, 8, this.devmaj) || this.needPax
149 this.needPax = encNumber(buf, off + 337, 8, this.devmin) || this.needPax
150 this.needPax = encString(buf, off + 345, prefixSize, prefix) || this.needPax
151 if (buf[off + 475] !== 0)
152 this.needPax = encString(buf, off + 345, 155, prefix) || this.needPax
153 else {
154 this.needPax = encString(buf, off + 345, 130, prefix) || this.needPax
155 this.needPax = encDate(buf, off + 476, 12, this.atime) || this.needPax
156 this.needPax = encDate(buf, off + 488, 12, this.ctime) || this.needPax
157 }
158
159 let sum = 8 * 0x20
160 for (let i = off; i < off + 148; i++)
161 sum += buf[i]
162
163 for (let i = off + 156; i < off + 512; i++)
164 sum += buf[i]
165
166 this.cksum = sum
167 encNumber(buf, off + 148, 8, this.cksum)
168 this.cksumValid = true
169
170 return this.needPax
171 }
172
173 set (data) {
174 for (const i in data) {
175 if (data[i] !== null && data[i] !== undefined)
176 this[i] = data[i]
177 }
178 }
179
180 get type () {
181 return types.name.get(this[TYPE]) || this[TYPE]
182 }
183
184 get typeKey () {
185 return this[TYPE]
186 }
187
188 set type (type) {
189 if (types.code.has(type))
190 this[TYPE] = types.code.get(type)
191 else
192 this[TYPE] = type
193 }
194}
195
196const splitPrefix = (p, prefixSize) => {
197 const pathSize = 100
198 let pp = p
199 let prefix = ''
200 let ret
201 const root = pathModule.parse(p).root || '.'
202
203 if (Buffer.byteLength(pp) < pathSize)
204 ret = [pp, prefix, false]
205 else {
206 // first set prefix to the dir, and path to the base
207 prefix = pathModule.dirname(pp)
208 pp = pathModule.basename(pp)
209
210 do {
211 // both fit!
212 if (Buffer.byteLength(pp) <= pathSize &&
213 Buffer.byteLength(prefix) <= prefixSize)
214 ret = [pp, prefix, false]
215
216 // prefix fits in prefix, but path doesn't fit in path
217 else if (Buffer.byteLength(pp) > pathSize &&
218 Buffer.byteLength(prefix) <= prefixSize)
219 ret = [pp.substr(0, pathSize - 1), prefix, true]
220
221 else {
222 // make path take a bit from prefix
223 pp = pathModule.join(pathModule.basename(prefix), pp)
224 prefix = pathModule.dirname(prefix)
225 }
226 } while (prefix !== root && !ret)
227
228 // at this point, found no resolution, just truncate
229 if (!ret)
230 ret = [p.substr(0, pathSize - 1), '', true]
231 }
232 return ret
233}
234
235const decString = (buf, off, size) =>
236 buf.slice(off, off + size).toString('utf8').replace(/\0.*/, '')
237
238const decDate = (buf, off, size) =>
239 numToDate(decNumber(buf, off, size))
240
241const numToDate = num => num === null ? null : new Date(num * 1000)
242
243const decNumber = (buf, off, size) =>
244 buf[off] & 0x80 ? large.parse(buf.slice(off, off + size))
245 : decSmallNumber(buf, off, size)
246
247const nanNull = value => isNaN(value) ? null : value
248
249const decSmallNumber = (buf, off, size) =>
250 nanNull(parseInt(
251 buf.slice(off, off + size)
252 .toString('utf8').replace(/\0.*$/, '').trim(), 8))
253
254// the maximum encodable as a null-terminated octal, by field size
255const MAXNUM = {
256 12: 0o77777777777,
257 8: 0o7777777,
258}
259
260const encNumber = (buf, off, size, number) =>
261 number === null ? false :
262 number > MAXNUM[size] || number < 0
263 ? (large.encode(number, buf.slice(off, off + size)), true)
264 : (encSmallNumber(buf, off, size, number), false)
265
266const encSmallNumber = (buf, off, size, number) =>
267 buf.write(octalString(number, size), off, size, 'ascii')
268
269const octalString = (number, size) =>
270 padOctal(Math.floor(number).toString(8), size)
271
272const padOctal = (string, size) =>
273 (string.length === size - 1 ? string
274 : new Array(size - string.length - 1).join('0') + string + ' ') + '\0'
275
276const encDate = (buf, off, size, date) =>
277 date === null ? false :
278 encNumber(buf, off, size, date.getTime() / 1000)
279
280// enough to fill the longest string we've got
281const NULLS = new Array(156).join('\0')
282// pad with nulls, return true if it's longer or non-ascii
283const encString = (buf, off, size, string) =>
284 string === null ? false :
285 (buf.write(string + NULLS, off, size, 'utf8'),
286 string.length !== Buffer.byteLength(string) || string.length > size)
287
288module.exports = Header
Note: See TracBrowser for help on using the repository browser.