Skip to content

Commit fae11aa

Browse files
committed
chore: cleaning up code
[ci skip]
1 parent 1b42bf4 commit fae11aa

File tree

12 files changed

+651
-500
lines changed

12 files changed

+651
-500
lines changed

src/Generator.ts

Lines changed: 50 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,11 @@
1-
import type { FileStat } from './types';
2-
import { GeneratorState, EntryType, HeaderSize, HeaderOffset } from './types';
1+
import type { FileType, FileStat } from './types';
2+
import { GeneratorState, EntryType } from './types';
33
import * as errors from './errors';
44
import * as utils from './utils';
55
import * as constants from './constants';
66

7-
function generateHeader(filePath: string, type: EntryType, stat: FileStat) {
8-
if (filePath.length > 255) {
9-
throw new errors.ErrorVirtualTarGeneratorInvalidFileName(
10-
'The file name must shorter than 255 characters',
11-
);
12-
}
13-
14-
// The time can be undefined, which would be referring to epoch 0.
15-
const time = utils.dateToUnixTime(stat.mtime ?? new Date(0));
16-
17-
const header = new Uint8Array(constants.BLOCK_SIZE);
18-
19-
// Every directory in tar must have a trailing /
20-
if (type === EntryType.DIRECTORY) {
21-
filePath = filePath.endsWith('/') ? filePath : filePath + '/';
22-
}
23-
24-
// If the length of the file path is less than 100 bytes, then we write it to
25-
// the file name. Otherwise, we write it into the file name prefix and append
26-
// file name to it.
27-
if (filePath.length < HeaderSize.FILE_NAME) {
28-
utils.writeBytesToArray(
29-
header,
30-
utils.splitFileName(filePath, 0, HeaderSize.FILE_NAME),
31-
HeaderOffset.FILE_NAME,
32-
HeaderSize.FILE_NAME,
33-
);
34-
} else {
35-
utils.writeBytesToArray(
36-
header,
37-
utils.splitFileName(
38-
filePath,
39-
HeaderSize.FILE_NAME,
40-
HeaderSize.FILE_NAME_PREFIX,
41-
),
42-
HeaderOffset.FILE_NAME,
43-
HeaderSize.FILE_NAME,
44-
);
45-
utils.writeBytesToArray(
46-
header,
47-
utils.splitFileName(filePath, 0, HeaderSize.FILE_NAME),
48-
HeaderOffset.FILE_NAME_PREFIX,
49-
HeaderSize.FILE_NAME_PREFIX,
50-
);
51-
}
52-
53-
// The file permissions, or the mode, is stored in the next chunk. This is
54-
// stored in an octal number format.
55-
utils.writeBytesToArray(
56-
header,
57-
utils.pad(stat.mode ?? '', HeaderSize.FILE_MODE, '0', '\0'),
58-
HeaderOffset.FILE_MODE,
59-
HeaderSize.FILE_MODE,
60-
);
61-
62-
// The owner UID is stored in this chunk
63-
utils.writeBytesToArray(
64-
header,
65-
utils.pad(stat.uid ?? '', HeaderSize.OWNER_UID, '0', '\0'),
66-
HeaderOffset.OWNER_UID,
67-
HeaderSize.OWNER_UID,
68-
);
69-
70-
// The owner GID is stored in this chunk
71-
utils.writeBytesToArray(
72-
header,
73-
utils.pad(stat.gid ?? '', HeaderSize.OWNER_GID, '0', '\0'),
74-
HeaderOffset.OWNER_GID,
75-
HeaderSize.OWNER_GID,
76-
);
77-
78-
// The file size is stored in this chunk. The file size must be zero for
79-
// directories, and it must be set for files.
80-
utils.writeBytesToArray(
81-
header,
82-
utils.pad(stat.size ?? '', HeaderSize.FILE_SIZE, '0', '\0'),
83-
HeaderOffset.FILE_SIZE,
84-
HeaderSize.FILE_SIZE,
85-
);
86-
87-
// The file mtime is stored in this chunk. As the mtime is not modified when
88-
// extracting a TAR file, the mtime can be preserved while still getting
89-
// deterministic archives.
90-
utils.writeBytesToArray(
91-
header,
92-
utils.pad(time, HeaderSize.FILE_MTIME, '0', '\0'),
93-
HeaderOffset.FILE_MTIME,
94-
HeaderSize.FILE_MTIME,
95-
);
96-
97-
// The checksum is calculated as the sum of all bytes in the header. It is
98-
// left blank for later calculation.
99-
100-
// The type of file is written as a single byte in the header.
101-
utils.writeBytesToArray(
102-
header,
103-
type,
104-
HeaderOffset.TYPE_FLAG,
105-
HeaderSize.TYPE_FLAG,
106-
);
107-
108-
// Link name will be null, as regular stat-ing cannot extract that
109-
// information.
110-
111-
// This value is the USTAR magic string which makes this file appear as
112-
// a tar file. Without this, the file cannot be parsed and extracted.
113-
utils.writeBytesToArray(
114-
header,
115-
constants.USTAR_NAME,
116-
HeaderOffset.USTAR_NAME,
117-
HeaderSize.USTAR_NAME,
118-
);
119-
120-
// This chunk stores the version of USTAR, which is '00' in this case.
121-
utils.writeBytesToArray(
122-
header,
123-
constants.USTAR_VERSION,
124-
HeaderOffset.USTAR_VERSION,
125-
HeaderSize.USTAR_VERSION,
126-
);
127-
128-
// Owner user name will be null, as regular stat-ing cannot extract this
129-
// information.
130-
131-
// Owner group name will be null, as regular stat-ing cannot extract this
132-
// information.
133-
134-
// Device major will be null, as this specific to linux kernel knowing what
135-
// drivers to use for executing certain files, and is irrelevant here.
136-
137-
// Device minor will be null, as this specific to linux kernel knowing what
138-
// drivers to use for executing certain files, and is irrelevant here.
139-
140-
// Updating with the new checksum
141-
const checksum = utils.calculateChecksum(header);
142-
143-
// Note the extra space in the padding for the checksum value. It is
144-
// intentionally placed there. The padding for checksum is ASCII spaces
145-
// instead of null, which is why it is used like this here.
146-
utils.writeBytesToArray(
147-
header,
148-
utils.pad(checksum, HeaderSize.CHECKSUM, '0', '\0'),
149-
HeaderOffset.CHECKSUM,
150-
HeaderSize.CHECKSUM,
151-
);
152-
153-
return header;
154-
}
155-
1567
/**
157-
* The TAR headers follow this structure
8+
* The TAR headers follow this structure:
1589
* Start Size Description
15910
* ------------------------------
16011
* 0 100 File name (first 100 bytes)
@@ -176,11 +27,55 @@ function generateHeader(filePath: string, type: EntryType, stat: FileStat) {
17627
* 500 12 '\0' (unused)
17728
*
17829
* Note that all numbers are in stringified octal format.
30+
*
31+
* The following data will be left blank (null):
32+
* - Link name
33+
* - Owner user name
34+
* - Owner group name
35+
* - Device major
36+
* - Device minor
37+
*
38+
* This is because this implementation does not interact with linked files.
39+
* Owner user name and group name cannot be extracted via regular stat-ing,
40+
* so it is left blank. In virtual situations, this field won't be useful
41+
* anyways. The device major and minor are specific to linux kernel, which
42+
* is not relevant to this virtual tar implementation. This is the reason
43+
* these fields have been left blank.
17944
*/
18045
class Generator {
18146
protected state: GeneratorState = GeneratorState.READY;
18247
protected remainingBytes = 0;
18348

49+
protected generateHeader(filePath: string, type: FileType, stat: FileStat) {
50+
if (filePath.length > 255) {
51+
throw new errors.ErrorVirtualTarGeneratorInvalidFileName(
52+
'The file name must shorter than 255 characters',
53+
);
54+
}
55+
56+
const header = new Uint8Array(constants.BLOCK_SIZE);
57+
58+
// Every directory in tar must have a trailing slash
59+
if (type === 'directory') {
60+
filePath = filePath.endsWith('/') ? filePath : filePath + '/';
61+
}
62+
63+
utils.writeUstarMagic(header);
64+
utils.writeFileType(header, type);
65+
utils.writeFilePath(header, filePath);
66+
utils.writeFileMode(header, stat.mode);
67+
utils.writeOwnerUid(header, stat.uid);
68+
utils.writeOwnerGid(header, stat.gid);
69+
utils.writeFileSize(header, stat.size);
70+
utils.writeFileMtime(header, stat.mtime);
71+
72+
// The checksum can only be calculated once the entire header has been
73+
// written. This is why the checksum is calculated and written at the end.
74+
utils.writeChecksum(header, utils.calculateChecksum(header));
75+
76+
return header;
77+
}
78+
18479
generateFile(filePath: string, stat: FileStat): Uint8Array {
18580
if (this.state === GeneratorState.READY) {
18681
// Make sure the size is valid
@@ -190,7 +85,7 @@ class Generator {
19085
);
19186
}
19287

193-
const generatedBlock = generateHeader(filePath, EntryType.FILE, stat);
88+
const generatedBlock = this.generateHeader(filePath, 'file', stat);
19489

19590
// If no data is in the file, then there is no need of a data block. It
19691
// will remain as READY.
@@ -217,7 +112,7 @@ class Generator {
217112
uid: stat.uid,
218113
gid: stat.gid,
219114
};
220-
return generateHeader(filePath, EntryType.DIRECTORY, directoryStat);
115+
return this.generateHeader(filePath, 'directory', directoryStat);
221116
}
222117
throw new errors.ErrorVirtualTarGeneratorInvalidState(
223118
`Expected state ${GeneratorState[GeneratorState.READY]} but got ${
@@ -230,7 +125,7 @@ class Generator {
230125
if (this.state === GeneratorState.READY) {
231126
this.state = GeneratorState.DATA;
232127
this.remainingBytes = size;
233-
return generateHeader('./PaxHeader', EntryType.EXTENDED, { size });
128+
return this.generateHeader('./PaxHeader', 'extended', { size });
234129
}
235130
throw new errors.ErrorVirtualTarGeneratorInvalidState(
236131
`Expected state ${GeneratorState[GeneratorState.READY]} but got ${

src/VirtualTar.ts

Whitespace-only changes.

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { default as Generator } from './Generator';
22
export { default as Parser } from './Parser';
3-
export * as contants from './constants';
3+
export * as constants from './constants';
44
export * as errors from './errors';
55
export * as utils from './utils';
6-
export * from './types';
6+
export * as types from './types';

src/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
type FileType = 'file' | 'directory' | 'extended';
2+
13
enum EntryType {
24
FILE = '0',
35
DIRECTORY = '5',
@@ -77,7 +79,7 @@ type TokenEnd = {
7779
type: 'end';
7880
};
7981

80-
enum FileType {
82+
enum _FileType {
8183
FILE,
8284
DIRECTORY,
8385
}
@@ -96,14 +98,14 @@ enum GeneratorState {
9698
ENDED,
9799
}
98100

99-
export type { FileStat, TokenHeader, TokenData, TokenEnd };
101+
export type { FileType, FileStat, TokenHeader, TokenData, TokenEnd };
100102

101103
export {
102104
EntryType,
103105
ExtendedHeaderKeywords,
104106
HeaderOffset,
105107
HeaderSize,
106-
FileType,
108+
_FileType,
107109
ParserState,
108110
GeneratorState,
109111
};

0 commit comments

Comments
 (0)