1 /* 2 * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "TarArchiveService.h" 8 9 #include <Directory.h> 10 #include <File.h> 11 #include <StringList.h> 12 13 #include "Logger.h" 14 #include "StorageUtils.h" 15 16 17 #define LENGTH_BLOCK 512 18 19 20 status_t 21 TarArchiveService::Unpack(BDataIO& tarDataIo, BPath& targetDirectory, 22 Stoppable* stoppable) 23 { 24 uint8 buffer[LENGTH_BLOCK]; 25 uint8 zero_buffer[LENGTH_BLOCK]; 26 status_t result = B_OK; 27 uint32_t count_items_read = 0; 28 29 HDINFO("will unpack to [%s]", targetDirectory.Path()); 30 31 memset(zero_buffer, 0, sizeof zero_buffer); 32 33 while (B_OK == result 34 && (NULL == stoppable || !stoppable->WasStopped()) 35 && B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) { 36 37 count_items_read++; 38 39 if (0 == memcmp(zero_buffer, buffer, sizeof zero_buffer)) { 40 HDDEBUG("detected end of tar-ball"); 41 return B_OK; // end of tar-ball. 42 } else { 43 TarArchiveHeader* header = TarArchiveHeader::CreateFromBlock( 44 buffer); 45 46 if (NULL == header) { 47 HDERROR("unable to parse a tar header"); 48 result = B_ERROR; 49 } 50 51 if (B_OK == result) 52 result = _UnpackItem(tarDataIo, targetDirectory, *header); 53 54 delete header; 55 } 56 } 57 58 HDERROR("did unpack %d tar items", count_items_read); 59 60 if (B_OK != result) { 61 HDERROR("error occurred unpacking tar items; %s", strerror(result)); 62 } 63 64 return result; 65 } 66 67 68 status_t 69 TarArchiveService::_EnsurePathToTarItemFile( 70 BPath& targetDirectoryPath, BString &tarItemPath) 71 { 72 if (tarItemPath.Length() == 0) 73 return B_ERROR; 74 75 BStringList components; 76 tarItemPath.Split("/", false, components); 77 78 for (int32 i = 0; i < components.CountStrings(); i++) { 79 BString component = components.StringAt(i); 80 81 if (_ValidatePathComponent(component) != B_OK) { 82 HDERROR("malformed component; [%s]", component.String()); 83 return B_ERROR; 84 } 85 } 86 87 BPath parentPath; 88 BPath assembledPath(targetDirectoryPath); 89 90 status_t result = assembledPath.Append(tarItemPath); 91 92 if (result == B_OK) 93 result = assembledPath.GetParent(&parentPath); 94 95 if (result == B_OK) 96 result = create_directory(parentPath.Path(), 0777); 97 98 return result; 99 } 100 101 102 status_t 103 TarArchiveService::_UnpackItem(BDataIO& tarDataIo, 104 BPath& targetDirectoryPath, 105 TarArchiveHeader& header) 106 { 107 status_t result = B_OK; 108 BString entryFileName = header.GetFileName(); 109 uint32 entryLength = header.GetLength(); 110 111 HDDEBUG("will unpack item [%s] length [%" B_PRIu32 "]b", 112 entryFileName.String(), entryLength); 113 114 // if the path ends in "/" then it is a directory and there's no need to 115 // unpack it although if there is a length, it will need to be skipped. 116 117 if (!entryFileName.EndsWith("/") || 118 header.GetFileType() != TAR_FILE_TYPE_NORMAL) { 119 120 result = _EnsurePathToTarItemFile(targetDirectoryPath, 121 entryFileName); 122 123 if (result == B_OK) { 124 BPath targetFilePath(targetDirectoryPath); 125 targetFilePath.Append(entryFileName, false); 126 result = _UnpackItemData(tarDataIo, targetFilePath, entryLength); 127 } 128 } else { 129 off_t blocksToSkip = (entryLength / LENGTH_BLOCK); 130 131 if (entryLength % LENGTH_BLOCK > 0) 132 blocksToSkip += 1; 133 134 if (0 != blocksToSkip) { 135 uint8 buffer[LENGTH_BLOCK]; 136 137 for (uint32 i = 0; B_OK == result && i < blocksToSkip; i++) 138 tarDataIo.ReadExactly(buffer, LENGTH_BLOCK); 139 } 140 } 141 142 return result; 143 } 144 145 146 status_t 147 TarArchiveService::_UnpackItemData(BDataIO& tarDataIo, 148 BPath& targetFilePath, uint32 length) 149 { 150 uint8 buffer[LENGTH_BLOCK]; 151 size_t remainingInItem = length; 152 status_t result = B_OK; 153 BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT); 154 155 while (remainingInItem > 0 && 156 B_OK == result && 157 B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) { 158 159 size_t writeFromBuffer = LENGTH_BLOCK; 160 161 if (remainingInItem < LENGTH_BLOCK) 162 writeFromBuffer = remainingInItem; 163 164 result = targetFile.WriteExactly(buffer, writeFromBuffer); 165 remainingInItem -= writeFromBuffer; 166 } 167 168 if (result != B_OK) { 169 HDERROR("unable to unpack item data to; [%s]", targetFilePath.Path()); 170 } 171 172 return result; 173 } 174 175 176 status_t 177 TarArchiveService::_ValidatePathComponent(const BString& component) 178 { 179 if (component.Length() == 0) 180 return B_ERROR; 181 182 if (component == ".." || component == "." || component == "~") 183 return B_ERROR; 184 185 return B_OK; 186 } 187 188