@@ -5,50 +5,86 @@ import * as constants from './constants';
55import * as errors from './errors' ;
66import * as utils from './utils' ;
77
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 ) ;
1147 const fileSize = utils . extractOctal (
12- view ,
48+ array ,
1349 HeaderOffset . FILE_SIZE ,
1450 HeaderSize . FILE_SIZE ,
1551 ) ;
1652 const fileMtime = new Date (
17- utils . extractOctal ( view , HeaderOffset . FILE_MTIME , HeaderSize . FILE_MTIME ) *
53+ utils . extractOctal ( array , HeaderOffset . FILE_MTIME , HeaderSize . FILE_MTIME ) *
1854 1000 ,
1955 ) ;
2056 const fileMode = utils . extractOctal (
21- view ,
57+ array ,
2258 HeaderOffset . FILE_MODE ,
2359 HeaderSize . FILE_MODE ,
2460 ) ;
2561 const ownerGid = utils . extractOctal (
26- view ,
62+ array ,
2763 HeaderOffset . OWNER_GID ,
2864 HeaderSize . OWNER_GID ,
2965 ) ;
3066 const ownerUid = utils . extractOctal (
31- view ,
67+ array ,
3268 HeaderOffset . OWNER_UID ,
3369 HeaderSize . OWNER_UID ,
3470 ) ;
3571 const ownerName = utils . extractString (
36- view ,
72+ array ,
3773 HeaderOffset . OWNER_NAME ,
3874 HeaderSize . OWNER_NAME ,
3975 ) ;
4076 const ownerGroupName = utils . extractString (
41- view ,
77+ array ,
4278 HeaderOffset . OWNER_GROUPNAME ,
4379 HeaderSize . OWNER_GROUPNAME ,
4480 ) ;
4581 const ownerUserName = utils . extractString (
46- view ,
82+ array ,
4783 HeaderOffset . OWNER_USERNAME ,
4884 HeaderSize . OWNER_USERNAME ,
4985 ) ;
5086 const fileType =
51- utils . extractString ( view , HeaderOffset . TYPE_FLAG , HeaderSize . TYPE_FLAG ) ===
87+ utils . extractString ( array , HeaderOffset . TYPE_FLAG , HeaderSize . TYPE_FLAG ) ===
5288 EntryType . FILE
5389 ? 'file'
5490 : 'directory' ;
@@ -68,11 +104,11 @@ function parseHeader(view: DataView): HeaderToken {
68104 } ;
69105}
70106
71- function parseData ( view : DataView , remainingBytes : number ) : DataToken {
107+ function parseData ( array : Uint8Array , remainingBytes : number ) : DataToken {
72108 if ( remainingBytes > 512 ) {
73- return { type : 'data' , data : utils . extractBytes ( view ) } ;
109+ return { type : 'data' , data : utils . extractBytes ( array ) } ;
74110 } else {
75- const data = utils . extractBytes ( view , 0 , remainingBytes ) ;
111+ const data = utils . extractBytes ( array , 0 , remainingBytes ) ;
76112 return { type : 'data' , data : data } ;
77113 }
78114}
@@ -83,29 +119,27 @@ class Parser {
83119
84120 write ( data : Uint8Array ) {
85121 if ( data . byteLength !== constants . BLOCK_SIZE ) {
86- throw new errors . ErrorVirtualTarBlockSize (
122+ throw new errors . ErrorTarParserBlockSize (
87123 `Expected block size to be ${ constants . BLOCK_SIZE } bytes but received ${ data . byteLength } bytes` ,
88124 ) ;
89125 }
90126
91- const view = new DataView ( data . buffer , 0 , constants . BLOCK_SIZE ) ;
92-
93127 switch ( this . state ) {
94128 case ParserState . ENDED : {
95- throw new errors . ErrorVirtualTarEndOfArchive (
129+ throw new errors . ErrorTarParserEndOfArchive (
96130 'Archive has already ended' ,
97131 ) ;
98132 }
99133
100134 case ParserState . READY : {
101135 // Check if we need to parse the end-of-archive marker
102- if ( utils . checkNullView ( view ) ) {
136+ if ( utils . isNullBlock ( data ) ) {
103137 this . state = ParserState . NULL ;
104138 return ;
105139 }
106140
107141 // Set relevant state if the header corresponds to a file
108- const headerToken = parseHeader ( view ) ;
142+ const headerToken = parseHeader ( data ) ;
109143 if ( headerToken . fileType === 'file' ) {
110144 this . state = ParserState . DATA ;
111145 this . remainingBytes = headerToken . fileSize ;
@@ -114,18 +148,18 @@ class Parser {
114148 }
115149
116150 case ParserState . DATA : {
117- const parsedData = parseData ( view , this . remainingBytes ) ;
151+ const parsedData = parseData ( data , this . remainingBytes ) ;
118152 this . remainingBytes -= 512 ;
119153 if ( this . remainingBytes < 0 ) this . state = ParserState . READY ;
120154 return parsedData ;
121155 }
122156
123157 case ParserState . NULL : {
124- if ( utils . checkNullView ( view ) ) {
158+ if ( utils . isNullBlock ( data ) ) {
125159 this . state = ParserState . ENDED ;
126160 return { type : 'end' } as EndToken ;
127161 } else {
128- throw new errors . ErrorVirtualTarEndOfArchive (
162+ throw new errors . ErrorTarParserEndOfArchive (
129163 'Received garbage data after first end marker' ,
130164 ) ;
131165 }
0 commit comments