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 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 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; 55f0e491d3SAndrew Lindesay uint32_t 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 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 119*963ebbcaSAndrew Lindesay /*static*/ int32 120*963ebbcaSAndrew Lindesay TarArchiveService::_ReadHeaderStringLength(const uint8* data, 121*963ebbcaSAndrew Lindesay size_t maxStringLength) 122f0e491d3SAndrew Lindesay { 123*963ebbcaSAndrew Lindesay int32 actualLength = 0; 124*963ebbcaSAndrew Lindesay while (actualLength < (int32) maxStringLength && data[actualLength] != 0) 125f0e491d3SAndrew Lindesay actualLength++; 126*963ebbcaSAndrew Lindesay return actualLength; 127*963ebbcaSAndrew Lindesay } 128*963ebbcaSAndrew Lindesay 129*963ebbcaSAndrew Lindesay 130*963ebbcaSAndrew Lindesay void 131*963ebbcaSAndrew Lindesay TarArchiveService::_ReadHeaderString(const uint8 *data, size_t maxStringLength, 132*963ebbcaSAndrew Lindesay BString& result) 133*963ebbcaSAndrew Lindesay { 134*963ebbcaSAndrew Lindesay result.SetTo((const char *) data, 135*963ebbcaSAndrew 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 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 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 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 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 211*963ebbcaSAndrew Lindesay BString fileName; 212*963ebbcaSAndrew Lindesay _ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME, fileName); 213*963ebbcaSAndrew Lindesay 214*963ebbcaSAndrew 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 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