vfs: check userland buffers before reading them.
[haiku.git] / src / apps / haikudepot / tar / TarArchiveService.cpp
blobc645003cfa9125a2b851de937d0057f308df859b
1 /*
2 * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "TarArchiveService.h"
8 #include <stdio.h>
10 #include <Directory.h>
11 #include <File.h>
12 #include <StringList.h>
14 #include "Logger.h"
15 #include "StorageUtils.h"
18 #define LENGTH_BLOCK 512
21 status_t
22 TarArchiveService::Unpack(BDataIO& tarDataIo, BPath& targetDirectory,
23 Stoppable* stoppable)
25 uint8 buffer[LENGTH_BLOCK];
26 uint8 zero_buffer[LENGTH_BLOCK];
27 status_t result = B_OK;
28 uint32_t count_items_read = 0;
30 fprintf(stdout, "will unpack to [%s]\n", targetDirectory.Path());
32 memset(zero_buffer, 0, sizeof zero_buffer);
34 while (B_OK == result
35 && (NULL == stoppable || !stoppable->WasStopped())
36 && B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) {
38 count_items_read++;
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);
47 if (NULL == header) {
48 fprintf(stderr, "unable to parse a tar header\n");
49 result = B_ERROR;
52 if (B_OK == result)
53 result = _UnpackItem(tarDataIo, targetDirectory, *header);
55 delete header;
59 fprintf(stdout, "did unpack %d tar items\n", count_items_read);
61 if (B_OK != result) {
62 fprintf(stdout, "error occurred unpacking tar items; %s\n",
63 strerror(result));
66 return result;
70 status_t
71 TarArchiveService::_EnsurePathToTarItemFile(
72 BPath& targetDirectoryPath, BString &tarItemPath)
74 if (tarItemPath.Length() == 0)
75 return B_ERROR;
77 BStringList components;
78 tarItemPath.Split("/", false, components);
80 for (int32 i = 0; i < components.CountStrings(); i++) {
81 BString component = components.StringAt(i);
83 if (_ValidatePathComponent(component) != B_OK) {
84 fprintf(stdout, "malformed component; [%s]\n", component.String());
85 return B_ERROR;
89 BPath parentPath;
90 BPath assembledPath(targetDirectoryPath);
92 status_t result = assembledPath.Append(tarItemPath);
94 if (result == B_OK)
95 result = assembledPath.GetParent(&parentPath);
97 if (result == B_OK)
98 result = create_directory(parentPath.Path(), 0777);
100 return result;
104 status_t
105 TarArchiveService::_UnpackItem(BDataIO& tarDataIo,
106 BPath& targetDirectoryPath,
107 TarArchiveHeader& header)
109 status_t result = B_OK;
110 BString entryFileName = header.GetFileName();
111 uint32 entryLength = header.GetLength();
113 if (Logger::IsDebugEnabled()) {
114 fprintf(stdout, "will unpack item [%s] length [%" B_PRIu32 "]b\n",
115 entryFileName.String(), entryLength);
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.
121 if (!entryFileName.EndsWith("/") ||
122 header.GetFileType() != TAR_FILE_TYPE_NORMAL) {
124 result = _EnsurePathToTarItemFile(targetDirectoryPath,
125 entryFileName);
127 if (result == B_OK) {
128 BPath targetFilePath(targetDirectoryPath);
129 targetFilePath.Append(entryFileName, false);
130 result = _UnpackItemData(tarDataIo, targetFilePath, entryLength);
132 } else {
133 off_t blocksToSkip = (entryLength / LENGTH_BLOCK);
135 if (entryLength % LENGTH_BLOCK > 0)
136 blocksToSkip += 1;
138 if (0 != blocksToSkip) {
139 uint8 buffer[LENGTH_BLOCK];
141 for (uint32 i = 0; B_OK == result && i < blocksToSkip; i++)
142 tarDataIo.ReadExactly(buffer, LENGTH_BLOCK);
146 return result;
150 status_t
151 TarArchiveService::_UnpackItemData(BDataIO& tarDataIo,
152 BPath& targetFilePath, uint32 length)
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);
159 while (remainingInItem > 0 &&
160 B_OK == result &&
161 B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) {
163 size_t writeFromBuffer = LENGTH_BLOCK;
165 if (remainingInItem < LENGTH_BLOCK)
166 writeFromBuffer = remainingInItem;
168 result = targetFile.WriteExactly(buffer, writeFromBuffer);
169 remainingInItem -= writeFromBuffer;
172 if (result != B_OK)
173 fprintf(stdout, "unable to unpack item data to; [%s]\n",
174 targetFilePath.Path());
176 return result;
180 status_t
181 TarArchiveService::_ValidatePathComponent(const BString& component)
183 if (component.Length() == 0)
184 return B_ERROR;
186 if (component == ".." || component == "." || component == "~")
187 return B_ERROR;
189 return B_OK;