@@ -5,50 +5,86 @@ import * as constants from './constants';
5
5
import * as errors from './errors' ;
6
6
import * as utils from './utils' ;
7
7
8
- function parseHeader ( view : DataView ) : HeaderToken {
9
- // TODO: confirm integrity by checking against checksum
10
- const filePath = utils . parseFilePath ( view ) ;
8
+ function parseHeader ( array : Uint8Array ) : HeaderToken {
9
+ // Validate header by checking checksum and magic string
10
+ const headerChecksum = utils . extractOctal (
11
+ array ,
12
+ HeaderOffset . CHECKSUM ,
13
+ HeaderSize . CHECKSUM ,
14
+ ) ;
15
+ const calculatedChecksum = utils . calculateChecksum ( array ) ;
16
+
17
+ if ( headerChecksum !== calculatedChecksum ) {
18
+ throw new errors . ErrorTarParserInvalidHeader (
19
+ `Expected checksum to be ${ calculatedChecksum } but received ${ headerChecksum } ` ,
20
+ ) ;
21
+ }
22
+
23
+ const ustarMagic = utils . extractString (
24
+ array ,
25
+ HeaderOffset . USTAR_NAME ,
26
+ HeaderSize . USTAR_NAME ,
27
+ ) ;
28
+ if ( ustarMagic !== constants . USTAR_NAME ) {
29
+ throw new errors . ErrorTarParserInvalidHeader (
30
+ `Expected ustar magic to be '${ constants . USTAR_NAME } ', got '${ ustarMagic } '` ,
31
+ ) ;
32
+ }
33
+
34
+ const ustarVersion = utils . extractString (
35
+ array ,
36
+ HeaderOffset . USTAR_VERSION ,
37
+ HeaderSize . USTAR_VERSION ,
38
+ ) ;
39
+ if ( ustarVersion !== constants . USTAR_VERSION ) {
40
+ throw new errors . ErrorTarParserInvalidHeader (
41
+ `Expected ustar version to be '${ constants . USTAR_VERSION } ', got '${ ustarVersion } '` ,
42
+ ) ;
43
+ }
44
+
45
+ // Extract the relevant metadata from the header
46
+ const filePath = utils . parseFilePath ( array ) ;
11
47
const fileSize = utils . extractOctal (
12
- view ,
48
+ array ,
13
49
HeaderOffset . FILE_SIZE ,
14
50
HeaderSize . FILE_SIZE ,
15
51
) ;
16
52
const fileMtime = new Date (
17
- utils . extractOctal ( view , HeaderOffset . FILE_MTIME , HeaderSize . FILE_MTIME ) *
53
+ utils . extractOctal ( array , HeaderOffset . FILE_MTIME , HeaderSize . FILE_MTIME ) *
18
54
1000 ,
19
55
) ;
20
56
const fileMode = utils . extractOctal (
21
- view ,
57
+ array ,
22
58
HeaderOffset . FILE_MODE ,
23
59
HeaderSize . FILE_MODE ,
24
60
) ;
25
61
const ownerGid = utils . extractOctal (
26
- view ,
62
+ array ,
27
63
HeaderOffset . OWNER_GID ,
28
64
HeaderSize . OWNER_GID ,
29
65
) ;
30
66
const ownerUid = utils . extractOctal (
31
- view ,
67
+ array ,
32
68
HeaderOffset . OWNER_UID ,
33
69
HeaderSize . OWNER_UID ,
34
70
) ;
35
71
const ownerName = utils . extractString (
36
- view ,
72
+ array ,
37
73
HeaderOffset . OWNER_NAME ,
38
74
HeaderSize . OWNER_NAME ,
39
75
) ;
40
76
const ownerGroupName = utils . extractString (
41
- view ,
77
+ array ,
42
78
HeaderOffset . OWNER_GROUPNAME ,
43
79
HeaderSize . OWNER_GROUPNAME ,
44
80
) ;
45
81
const ownerUserName = utils . extractString (
46
- view ,
82
+ array ,
47
83
HeaderOffset . OWNER_USERNAME ,
48
84
HeaderSize . OWNER_USERNAME ,
49
85
) ;
50
86
const fileType =
51
- utils . extractString ( view , HeaderOffset . TYPE_FLAG , HeaderSize . TYPE_FLAG ) ===
87
+ utils . extractString ( array , HeaderOffset . TYPE_FLAG , HeaderSize . TYPE_FLAG ) ===
52
88
EntryType . FILE
53
89
? 'file'
54
90
: 'directory' ;
@@ -68,11 +104,11 @@ function parseHeader(view: DataView): HeaderToken {
68
104
} ;
69
105
}
70
106
71
- function parseData ( view : DataView , remainingBytes : number ) : DataToken {
107
+ function parseData ( array : Uint8Array , remainingBytes : number ) : DataToken {
72
108
if ( remainingBytes > 512 ) {
73
- return { type : 'data' , data : utils . extractBytes ( view ) } ;
109
+ return { type : 'data' , data : utils . extractBytes ( array ) } ;
74
110
} else {
75
- const data = utils . extractBytes ( view , 0 , remainingBytes ) ;
111
+ const data = utils . extractBytes ( array , 0 , remainingBytes ) ;
76
112
return { type : 'data' , data : data } ;
77
113
}
78
114
}
@@ -83,29 +119,27 @@ class Parser {
83
119
84
120
write ( data : Uint8Array ) {
85
121
if ( data . byteLength !== constants . BLOCK_SIZE ) {
86
- throw new errors . ErrorVirtualTarBlockSize (
122
+ throw new errors . ErrorTarParserBlockSize (
87
123
`Expected block size to be ${ constants . BLOCK_SIZE } bytes but received ${ data . byteLength } bytes` ,
88
124
) ;
89
125
}
90
126
91
- const view = new DataView ( data . buffer , 0 , constants . BLOCK_SIZE ) ;
92
-
93
127
switch ( this . state ) {
94
128
case ParserState . ENDED : {
95
- throw new errors . ErrorVirtualTarEndOfArchive (
129
+ throw new errors . ErrorTarParserEndOfArchive (
96
130
'Archive has already ended' ,
97
131
) ;
98
132
}
99
133
100
134
case ParserState . READY : {
101
135
// Check if we need to parse the end-of-archive marker
102
- if ( utils . checkNullView ( view ) ) {
136
+ if ( utils . isNullBlock ( data ) ) {
103
137
this . state = ParserState . NULL ;
104
138
return ;
105
139
}
106
140
107
141
// Set relevant state if the header corresponds to a file
108
- const headerToken = parseHeader ( view ) ;
142
+ const headerToken = parseHeader ( data ) ;
109
143
if ( headerToken . fileType === 'file' ) {
110
144
this . state = ParserState . DATA ;
111
145
this . remainingBytes = headerToken . fileSize ;
@@ -114,18 +148,18 @@ class Parser {
114
148
}
115
149
116
150
case ParserState . DATA : {
117
- const parsedData = parseData ( view , this . remainingBytes ) ;
151
+ const parsedData = parseData ( data , this . remainingBytes ) ;
118
152
this . remainingBytes -= 512 ;
119
153
if ( this . remainingBytes < 0 ) this . state = ParserState . READY ;
120
154
return parsedData ;
121
155
}
122
156
123
157
case ParserState . NULL : {
124
- if ( utils . checkNullView ( view ) ) {
158
+ if ( utils . isNullBlock ( data ) ) {
125
159
this . state = ParserState . ENDED ;
126
160
return { type : 'end' } as EndToken ;
127
161
} else {
128
- throw new errors . ErrorVirtualTarEndOfArchive (
162
+ throw new errors . ErrorTarParserEndOfArchive (
129
163
'Received garbage data after first end marker' ,
130
164
) ;
131
165
}
0 commit comments