2 * Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "TarArchiveService.h"
10 #include <Directory.h>
12 #include <StringList.h>
15 #include "StorageUtils.h"
18 #define LENGTH_BLOCK 512
22 TarArchiveService::Unpack(BDataIO
& tarDataIo
, BPath
& targetDirectory
,
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
);
35 && (NULL
== stoppable
|| !stoppable
->WasStopped())
36 && B_OK
== (result
= tarDataIo
.ReadExactly(buffer
, LENGTH_BLOCK
))) {
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.
44 TarArchiveHeader
* header
= TarArchiveHeader::CreateFromBlock(
48 fprintf(stderr
, "unable to parse a tar header\n");
53 result
= _UnpackItem(tarDataIo
, targetDirectory
, *header
);
59 fprintf(stdout
, "did unpack %d tar items\n", count_items_read
);
62 fprintf(stdout
, "error occurred unpacking tar items; %s\n",
71 TarArchiveService::_EnsurePathToTarItemFile(
72 BPath
& targetDirectoryPath
, BString
&tarItemPath
)
74 if (tarItemPath
.Length() == 0)
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());
90 BPath
assembledPath(targetDirectoryPath
);
92 status_t result
= assembledPath
.Append(tarItemPath
);
95 result
= assembledPath
.GetParent(&parentPath
);
98 result
= create_directory(parentPath
.Path(), 0777);
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
,
127 if (result
== B_OK
) {
128 BPath
targetFilePath(targetDirectoryPath
);
129 targetFilePath
.Append(entryFileName
, false);
130 result
= _UnpackItemData(tarDataIo
, targetFilePath
, entryLength
);
133 off_t blocksToSkip
= (entryLength
/ LENGTH_BLOCK
);
135 if (entryLength
% LENGTH_BLOCK
> 0)
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
);
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 &&
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
;
173 fprintf(stdout
, "unable to unpack item data to; [%s]\n",
174 targetFilePath
.Path());
181 TarArchiveService::_ValidatePathComponent(const BString
& component
)
183 if (component
.Length() == 0)
186 if (component
== ".." || component
== "." || component
== "~")