xref: /haiku/src/apps/haikudepot/tar/TarArchiveService.cpp (revision b72cc7f40573ac9d0f4a0b3b1be47f38ce89389c)
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