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 { 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 fprintf(stdout, "will unpack to [%s]\n", targetDirectory.Path()); 30 31 memset(zero_buffer, 0, sizeof zero_buffer); 32 33 while (B_OK == result && B_OK == (result = tarDataIo.ReadExactly(buffer, 34 LENGTH_BLOCK))) { 35 36 count_items_read++; 37 38 if (0 == memcmp(zero_buffer, buffer, sizeof zero_buffer)) { 39 fprintf(stdout, "detected end of tar-ball\n"); 40 return B_OK; // end of tar-ball. 41 } else { 42 TarArchiveHeader* header = TarArchiveHeader::CreateFromBlock( 43 buffer); 44 45 if (NULL == header) { 46 fprintf(stderr, "unable to parse a tar header\n"); 47 result = B_ERROR; 48 } 49 50 if (B_OK == result) 51 result = _UnpackItem(tarDataIo, targetDirectory, *header); 52 53 delete header; 54 } 55 } 56 57 fprintf(stdout, "did unpack %d tar items\n", count_items_read); 58 59 if (B_OK != result) { 60 fprintf(stdout, "error occurred unpacking tar items; %s\n", 61 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 fprintf(stdout, "malformed component; [%s]\n", 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 if (Logger::IsDebugEnabled()) { 112 fprintf(stdout, "will unpack item [%s] length [%" B_PRIu32 "]b\n", 113 entryFileName.String(), entryLength); 114 } 115 116 // if the path ends in "/" then it is a directory and there's no need to 117 // unpack it although if there is a length, it will need to be skipped. 118 119 if (!entryFileName.EndsWith("/") || 120 header.GetFileType() != TAR_FILE_TYPE_NORMAL) { 121 122 result = _EnsurePathToTarItemFile(targetDirectoryPath, 123 entryFileName); 124 125 if (result == B_OK) { 126 BPath targetFilePath(targetDirectoryPath); 127 targetFilePath.Append(entryFileName, false); 128 result = _UnpackItemData(tarDataIo, targetFilePath, entryLength); 129 } 130 } else { 131 off_t blocksToSkip = (entryLength / LENGTH_BLOCK); 132 133 if (entryLength % LENGTH_BLOCK > 0) 134 blocksToSkip += 1; 135 136 if (0 != blocksToSkip) { 137 uint8 buffer[LENGTH_BLOCK]; 138 139 for (uint32 i = 0; B_OK == result && i < blocksToSkip; i++) 140 tarDataIo.ReadExactly(buffer, LENGTH_BLOCK); 141 } 142 } 143 144 return result; 145 } 146 147 148 status_t 149 TarArchiveService::_UnpackItemData(BDataIO& tarDataIo, 150 BPath& targetFilePath, uint32 length) 151 { 152 uint8 buffer[LENGTH_BLOCK]; 153 size_t remainingInItem = length; 154 status_t result = B_OK; 155 BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT); 156 157 while (remainingInItem > 0 && 158 B_OK == result && 159 B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) { 160 161 size_t writeFromBuffer = LENGTH_BLOCK; 162 163 if (remainingInItem < LENGTH_BLOCK) 164 writeFromBuffer = remainingInItem; 165 166 result = targetFile.WriteExactly(buffer, writeFromBuffer); 167 remainingInItem -= writeFromBuffer; 168 } 169 170 if (result != B_OK) 171 fprintf(stdout, "unable to unpack item data to; [%s]\n", 172 targetFilePath.Path()); 173 174 return result; 175 } 176 177 178 status_t 179 TarArchiveService::_ValidatePathComponent(const BString& component) 180 { 181 if (component.Length() == 0) 182 return B_ERROR; 183 184 if (component == ".." || component == "." || component == "~") 185 return B_ERROR; 186 187 return B_OK; 188 } 189 190