119c15fecSAndrew Lindesay /*
2f96d1f4dSAndrew Lindesay * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
319c15fecSAndrew Lindesay * All rights reserved. Distributed under the terms of the MIT License.
419c15fecSAndrew Lindesay */
519c15fecSAndrew Lindesay #include "TarArchiveService.h"
619c15fecSAndrew Lindesay
7f0e491d3SAndrew Lindesay #include <AutoDeleter.h>
819c15fecSAndrew Lindesay #include <Directory.h>
919c15fecSAndrew Lindesay #include <File.h>
1019c15fecSAndrew Lindesay #include <StringList.h>
1119c15fecSAndrew Lindesay
12f0e491d3SAndrew Lindesay #include "DataIOUtils.h"
13f0665db4SAndrew Lindesay #include "Logger.h"
14f0665db4SAndrew Lindesay #include "StorageUtils.h"
15f0665db4SAndrew Lindesay
1619c15fecSAndrew Lindesay
17f0e491d3SAndrew Lindesay #define OFFSET_FILENAME 0
18f0e491d3SAndrew Lindesay #define OFFSET_LENGTH 124
19f0e491d3SAndrew Lindesay #define OFFSET_CHECKSUM 148
20f0e491d3SAndrew Lindesay #define OFFSET_FILETYPE 156
21f0e491d3SAndrew Lindesay
22f0e491d3SAndrew Lindesay #define LENGTH_FILENAME 100
23f0e491d3SAndrew Lindesay #define LENGTH_LENGTH 12
24f0e491d3SAndrew Lindesay #define LENGTH_CHECKSUM 8
2519c15fecSAndrew Lindesay #define LENGTH_BLOCK 512
2619c15fecSAndrew Lindesay
2719c15fecSAndrew Lindesay
28f0e491d3SAndrew Lindesay /*! This method will parse the header that is located at the current position of
29f0e491d3SAndrew Lindesay the supplied tarIo. Upon success, the tarIo will point to the start of the
30f0e491d3SAndrew Lindesay data for the parsed entry.
31f0e491d3SAndrew Lindesay */
32f0e491d3SAndrew Lindesay
33f0e491d3SAndrew Lindesay /*static*/ status_t
GetEntry(BPositionIO & tarIo,TarArchiveHeader & header)34f0e491d3SAndrew Lindesay TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header)
35f0e491d3SAndrew Lindesay {
36f0e491d3SAndrew Lindesay uint8 buffer[LENGTH_BLOCK];
37f0e491d3SAndrew Lindesay status_t result = tarIo.ReadExactly(buffer, LENGTH_BLOCK);
38f0e491d3SAndrew Lindesay if (result == B_OK)
39f0e491d3SAndrew Lindesay result = _ReadHeader(buffer, header);
40f0e491d3SAndrew Lindesay return result;
41f0e491d3SAndrew Lindesay }
42f0e491d3SAndrew Lindesay
43f0e491d3SAndrew Lindesay
44f0e491d3SAndrew Lindesay /*! For each entry in the tar file, this method will call the listener. If the
45f0e491d3SAndrew Lindesay listener does not return B_OK then the process will stop. This method is
46f0e491d3SAndrew Lindesay useful for obtaining a catalog of items in the tar file for example.
47f0e491d3SAndrew Lindesay */
48f0e491d3SAndrew Lindesay
49f0e491d3SAndrew Lindesay /*status*/ status_t
ForEachEntry(BPositionIO & tarIo,TarEntryListener * listener)50f0e491d3SAndrew Lindesay TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener)
5119c15fecSAndrew Lindesay {
5219c15fecSAndrew Lindesay uint8 buffer[LENGTH_BLOCK];
5319c15fecSAndrew Lindesay uint8 zero_buffer[LENGTH_BLOCK];
5419c15fecSAndrew Lindesay status_t result = B_OK;
55*b72cc7f4SAndrew Lindesay uint32 countItemsRead = 0;
56f0e491d3SAndrew Lindesay off_t offset = 0;
5719c15fecSAndrew Lindesay
5819c15fecSAndrew Lindesay memset(zero_buffer, 0, sizeof zero_buffer);
59f0e491d3SAndrew Lindesay tarIo.Seek(offset, SEEK_SET);
6019c15fecSAndrew Lindesay
61f0e491d3SAndrew Lindesay while (result == B_OK
62f0e491d3SAndrew Lindesay && B_OK == (result = tarIo.ReadExactly(
63f0e491d3SAndrew Lindesay buffer, LENGTH_BLOCK))) {
6419c15fecSAndrew Lindesay
65f0e491d3SAndrew Lindesay if (memcmp(zero_buffer, buffer, sizeof zero_buffer) == 0) {
66f0e491d3SAndrew Lindesay HDDEBUG("detected end of tar-ball after %" B_PRIu32 " items",
67f0e491d3SAndrew Lindesay countItemsRead);
6819c15fecSAndrew Lindesay return B_OK; // end of tar-ball.
6919c15fecSAndrew Lindesay }
7019c15fecSAndrew Lindesay
71f0e491d3SAndrew Lindesay TarArchiveHeader header;
72f0e491d3SAndrew Lindesay result = _ReadHeader(buffer, header);
7319c15fecSAndrew Lindesay
74f0e491d3SAndrew Lindesay HDTRACE("did read tar entry header for [%s]",
75f0e491d3SAndrew Lindesay header.FileName().String());
7619c15fecSAndrew Lindesay
7719c15fecSAndrew Lindesay if (result == B_OK) {
78f0e491d3SAndrew Lindesay countItemsRead++;
79f0e491d3SAndrew Lindesay
80f0e491d3SAndrew Lindesay // call out to the listener to read the data from the entry
81f0e491d3SAndrew Lindesay // and/or just process the header information.
82f0e491d3SAndrew Lindesay
83f0e491d3SAndrew Lindesay if (listener != NULL) {
84f0e491d3SAndrew Lindesay BDataIO *entryData = new ConstraintedDataIO(&tarIo,
85f0e491d3SAndrew Lindesay header.Length());
86f0e491d3SAndrew Lindesay ObjectDeleter<BDataIO> entryDataDeleter(entryData);
87f0e491d3SAndrew Lindesay result = listener->Handle(header, offset, entryData);
8819c15fecSAndrew Lindesay }
8919c15fecSAndrew Lindesay }
90f0e491d3SAndrew Lindesay
91f0e491d3SAndrew Lindesay offset += LENGTH_BLOCK;
92f0e491d3SAndrew Lindesay offset += _BytesRoundedToBlocks(header.Length());
93f0e491d3SAndrew Lindesay
94f0e491d3SAndrew Lindesay if (result == B_OK)
95f0e491d3SAndrew Lindesay tarIo.Seek(offset, SEEK_SET);
96f0e491d3SAndrew Lindesay }
97f0e491d3SAndrew Lindesay
98f0e491d3SAndrew Lindesay if (result == B_OK || result == B_CANCELED)
99f0e491d3SAndrew Lindesay HDINFO("did list %" B_PRIu32 " tar items", countItemsRead);
100f0e491d3SAndrew Lindesay else
101f0e491d3SAndrew Lindesay HDERROR("error occurred listing tar items; %s", strerror(result));
102f0e491d3SAndrew Lindesay
103f0e491d3SAndrew Lindesay return result;
104f0e491d3SAndrew Lindesay }
105f0e491d3SAndrew Lindesay
106f0e491d3SAndrew Lindesay
107f0e491d3SAndrew Lindesay tar_file_type
_ReadHeaderFileType(unsigned char data)108f0e491d3SAndrew Lindesay TarArchiveService::_ReadHeaderFileType(unsigned char data) {
109f0e491d3SAndrew Lindesay switch (data) {
110f0e491d3SAndrew Lindesay case 0:
111f0e491d3SAndrew Lindesay case '0':
112f0e491d3SAndrew Lindesay return TAR_FILE_TYPE_NORMAL;
113f0e491d3SAndrew Lindesay default:
114f0e491d3SAndrew Lindesay return TAR_FILE_TYPE_OTHER;
115f0e491d3SAndrew Lindesay }
116f0e491d3SAndrew Lindesay }
117f0e491d3SAndrew Lindesay
118f0e491d3SAndrew Lindesay
119963ebbcaSAndrew Lindesay /*static*/ int32
_ReadHeaderStringLength(const uint8 * data,size_t maxStringLength)120963ebbcaSAndrew Lindesay TarArchiveService::_ReadHeaderStringLength(const uint8* data,
121963ebbcaSAndrew Lindesay size_t maxStringLength)
122f0e491d3SAndrew Lindesay {
123963ebbcaSAndrew Lindesay int32 actualLength = 0;
124963ebbcaSAndrew Lindesay while (actualLength < (int32) maxStringLength && data[actualLength] != 0)
125f0e491d3SAndrew Lindesay actualLength++;
126963ebbcaSAndrew Lindesay return actualLength;
127963ebbcaSAndrew Lindesay }
128963ebbcaSAndrew Lindesay
129963ebbcaSAndrew Lindesay
130963ebbcaSAndrew Lindesay void
_ReadHeaderString(const uint8 * data,size_t maxStringLength,BString & result)131963ebbcaSAndrew Lindesay TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength,
132963ebbcaSAndrew Lindesay BString& result)
133963ebbcaSAndrew Lindesay {
134963ebbcaSAndrew Lindesay result.SetTo((const char *) data,
135963ebbcaSAndrew Lindesay _ReadHeaderStringLength(data, maxStringLength));
136f0e491d3SAndrew Lindesay }
137f0e491d3SAndrew Lindesay
138f0e491d3SAndrew Lindesay
139f0e491d3SAndrew Lindesay /*! This function will return true if the character supplied is a valid
140f0e491d3SAndrew Lindesay character in an number expressed in octal.
141f0e491d3SAndrew Lindesay */
142f0e491d3SAndrew Lindesay
tar_is_octal_digit(unsigned char c)143f0e491d3SAndrew Lindesay static bool tar_is_octal_digit(unsigned char c)
144f0e491d3SAndrew Lindesay {
145f0e491d3SAndrew Lindesay switch (c) {
146f0e491d3SAndrew Lindesay case '0':
147f0e491d3SAndrew Lindesay case '1':
148f0e491d3SAndrew Lindesay case '2':
149f0e491d3SAndrew Lindesay case '3':
150f0e491d3SAndrew Lindesay case '4':
151f0e491d3SAndrew Lindesay case '5':
152f0e491d3SAndrew Lindesay case '6':
153f0e491d3SAndrew Lindesay case '7':
154f0e491d3SAndrew Lindesay return true;
155f0e491d3SAndrew Lindesay default:
156f0e491d3SAndrew Lindesay return false;
157f0e491d3SAndrew Lindesay }
158f0e491d3SAndrew Lindesay }
159f0e491d3SAndrew Lindesay
160f0e491d3SAndrew Lindesay
161f0e491d3SAndrew Lindesay /*static*/ uint32
_ReadHeaderNumeric(const uint8 * data,size_t dataLength)162f0e491d3SAndrew Lindesay TarArchiveService::_ReadHeaderNumeric(const uint8 *data, size_t dataLength)
163f0e491d3SAndrew Lindesay {
164f0e491d3SAndrew Lindesay uint32 actualLength = 0;
165f0e491d3SAndrew Lindesay
166f0e491d3SAndrew Lindesay while (actualLength < dataLength && tar_is_octal_digit(data[actualLength]))
167f0e491d3SAndrew Lindesay actualLength++;
168f0e491d3SAndrew Lindesay
169f0e491d3SAndrew Lindesay uint32 factor = 1;
170f0e491d3SAndrew Lindesay uint32 result = 0;
171f0e491d3SAndrew Lindesay
172f0e491d3SAndrew Lindesay for (uint32 i = 0; i < actualLength; i++) {
173f0e491d3SAndrew Lindesay result += (data[actualLength - (1 + i)] - '0') * factor;
174f0e491d3SAndrew Lindesay factor *= 8;
17519c15fecSAndrew Lindesay }
17619c15fecSAndrew Lindesay
17719c15fecSAndrew Lindesay return result;
17819c15fecSAndrew Lindesay }
17919c15fecSAndrew Lindesay
18019c15fecSAndrew Lindesay
181f0e491d3SAndrew Lindesay /*static*/ uint32
_CalculateBlockChecksum(const uint8 * block)182f0e491d3SAndrew Lindesay TarArchiveService::_CalculateBlockChecksum(const uint8* block)
18319c15fecSAndrew Lindesay {
184f0e491d3SAndrew Lindesay uint32 result = 0;
18519c15fecSAndrew Lindesay
186f0e491d3SAndrew Lindesay for (uint32 i = 0; i < LENGTH_BLOCK; i++) {
187f0e491d3SAndrew Lindesay if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM)
188f0e491d3SAndrew Lindesay result += 32;
189f0e491d3SAndrew Lindesay else
190f0e491d3SAndrew Lindesay result += (uint32) block[i];
191fa5c8097SAndrew Lindesay }
19219c15fecSAndrew Lindesay
19319c15fecSAndrew Lindesay return result;
19419c15fecSAndrew Lindesay }
19519c15fecSAndrew Lindesay
19619c15fecSAndrew Lindesay
197f0e491d3SAndrew Lindesay /*static*/ status_t
_ReadHeader(const uint8 * block,TarArchiveHeader & header)198f0e491d3SAndrew Lindesay TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header)
19919c15fecSAndrew Lindesay {
200f0e491d3SAndrew Lindesay uint32 actualChecksum = _CalculateBlockChecksum(block);
201f0e491d3SAndrew Lindesay uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM],
202f0e491d3SAndrew Lindesay LENGTH_CHECKSUM);
20319c15fecSAndrew Lindesay
204f0e491d3SAndrew Lindesay if(actualChecksum != expectedChecksum) {
205f0e491d3SAndrew Lindesay HDERROR("tar archive header has bad checksum;"
206f0e491d3SAndrew Lindesay "expected %" B_PRIu32 " actual %" B_PRIu32,
207f0e491d3SAndrew Lindesay expectedChecksum, actualChecksum);
208f0e491d3SAndrew Lindesay return B_BAD_DATA;
209f0e491d3SAndrew Lindesay }
210f0e491d3SAndrew Lindesay
211963ebbcaSAndrew Lindesay BString fileName;
212963ebbcaSAndrew Lindesay _ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME, fileName);
213963ebbcaSAndrew Lindesay
214963ebbcaSAndrew Lindesay header.SetFileName(fileName);
215f0e491d3SAndrew Lindesay header.SetLength(
216f0e491d3SAndrew Lindesay _ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH));
217f0e491d3SAndrew Lindesay header.SetFileType(
218f0e491d3SAndrew Lindesay _ReadHeaderFileType(block[OFFSET_FILETYPE]));
21919c15fecSAndrew Lindesay
22019c15fecSAndrew Lindesay return B_OK;
22119c15fecSAndrew Lindesay }
22219c15fecSAndrew Lindesay
223f0e491d3SAndrew Lindesay
224f0e491d3SAndrew Lindesay /*static*/ off_t
_BytesRoundedToBlocks(off_t value)225f0e491d3SAndrew Lindesay TarArchiveService::_BytesRoundedToBlocks(off_t value)
226f0e491d3SAndrew Lindesay {
227f0e491d3SAndrew Lindesay if (0 != value % LENGTH_BLOCK)
228f0e491d3SAndrew Lindesay return ((value / LENGTH_BLOCK) + 1) * LENGTH_BLOCK;
229f0e491d3SAndrew Lindesay return (value / LENGTH_BLOCK) * LENGTH_BLOCK;
230f0e491d3SAndrew Lindesay }
231