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