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