/src/gdal/port/cpl_vsil_tar.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: CPL - Common Portability Library |
4 | | * Purpose: Implement VSI large file api for tar files (.tar). |
5 | | * Author: Even Rouault, even.rouault at spatialys.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | //! @cond Doxygen_Suppress |
14 | | |
15 | | #include "cpl_port.h" |
16 | | #include "cpl_vsi.h" |
17 | | |
18 | | #include <cstring> |
19 | | |
20 | | #include <fcntl.h> |
21 | | |
22 | | #include <string> |
23 | | #include <string_view> |
24 | | #include <vector> |
25 | | |
26 | | #include "cpl_conv.h" |
27 | | #include "cpl_error.h" |
28 | | #include "cpl_string.h" |
29 | | #include "cpl_vsi_virtual.h" |
30 | | |
31 | | #if (defined(DEBUG) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)) && \ |
32 | | !defined(HAVE_FUZZER_FRIENDLY_ARCHIVE) |
33 | | /* This is a completely custom archive format that is rather inefficient */ |
34 | | /* but supports random insertions or deletions, since it doesn't record */ |
35 | | /* explicit file size or rely on files starting on a particular boundary */ |
36 | | #define HAVE_FUZZER_FRIENDLY_ARCHIVE 1 |
37 | | #endif |
38 | | |
39 | | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
40 | | constexpr int HALF_BUFFER_SIZE = 1024; |
41 | | constexpr int BUFFER_SIZE = 2 * HALF_BUFFER_SIZE; |
42 | | #endif |
43 | | |
44 | | /************************************************************************/ |
45 | | /* ==================================================================== */ |
46 | | /* VSITarEntryFileOffset */ |
47 | | /* ==================================================================== */ |
48 | | /************************************************************************/ |
49 | | |
50 | | class VSITarEntryFileOffset final : public VSIArchiveEntryFileOffset |
51 | | { |
52 | | public: |
53 | | GUIntBig m_nOffset = 0; |
54 | | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
55 | | GUIntBig m_nFileSize = 0; |
56 | | CPLString m_osFileName{}; |
57 | | #endif |
58 | | |
59 | 0 | explicit VSITarEntryFileOffset(GUIntBig nOffset) : m_nOffset(nOffset) |
60 | 0 | { |
61 | 0 | } |
62 | | |
63 | | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
64 | | VSITarEntryFileOffset(GUIntBig nOffset, GUIntBig nFileSize, |
65 | | const CPLString &osFileName) |
66 | 0 | : m_nOffset(nOffset), m_nFileSize(nFileSize), m_osFileName(osFileName) |
67 | 0 | { |
68 | 0 | } |
69 | | #endif |
70 | | |
71 | | ~VSITarEntryFileOffset() override; |
72 | | }; |
73 | | |
74 | 0 | VSITarEntryFileOffset::~VSITarEntryFileOffset() = default; |
75 | | |
76 | | /************************************************************************/ |
77 | | /* ==================================================================== */ |
78 | | /* VSITarReader */ |
79 | | /* ==================================================================== */ |
80 | | /************************************************************************/ |
81 | | |
82 | | class VSITarReader final : public VSIArchiveReader |
83 | | { |
84 | | private: |
85 | | CPL_DISALLOW_COPY_ASSIGN(VSITarReader) |
86 | | |
87 | | VSILFILE *fp = nullptr; |
88 | | GUIntBig nCurOffset = 0; |
89 | | GUIntBig nNextFileSize = 0; |
90 | | CPLString osNextFileName{}; |
91 | | GIntBig nModifiedTime = 0; |
92 | | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
93 | | bool m_bIsFuzzerFriendly = false; |
94 | | GByte m_abyBuffer[BUFFER_SIZE + 1] = {}; |
95 | | int m_abyBufferIdx = 0; |
96 | | int m_abyBufferSize = 0; |
97 | | GUIntBig m_nCurOffsetOld = 0; |
98 | | #endif |
99 | | |
100 | | public: |
101 | | explicit VSITarReader(const char *pszTarFileName); |
102 | | ~VSITarReader() override; |
103 | | |
104 | | int IsValid() |
105 | 656 | { |
106 | 656 | return fp != nullptr; |
107 | 656 | } |
108 | | |
109 | | int GotoFirstFile() override; |
110 | | int GotoNextFile() override; |
111 | | VSIArchiveEntryFileOffset *GetFileOffset() override; |
112 | | |
113 | | GUIntBig GetFileSize() override |
114 | 0 | { |
115 | 0 | return nNextFileSize; |
116 | 0 | } |
117 | | |
118 | | CPLString GetFileName() override |
119 | 0 | { |
120 | 0 | return osNextFileName; |
121 | 0 | } |
122 | | |
123 | | GIntBig GetModifiedTime() override |
124 | 0 | { |
125 | 0 | return nModifiedTime; |
126 | 0 | } |
127 | | |
128 | | int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override; |
129 | | }; |
130 | | |
131 | | /************************************************************************/ |
132 | | /* VSIIsTGZ() */ |
133 | | /************************************************************************/ |
134 | | |
135 | | static bool VSIIsTGZ(const char *pszFilename) |
136 | 656 | { |
137 | 656 | return ( |
138 | 656 | !STARTS_WITH_CI(pszFilename, "/vsigzip/") && |
139 | 656 | ((strlen(pszFilename) > 4 && |
140 | 559 | STARTS_WITH_CI(pszFilename + strlen(pszFilename) - 4, ".tgz")) || |
141 | 559 | (strlen(pszFilename) > 7 && |
142 | 558 | STARTS_WITH_CI(pszFilename + strlen(pszFilename) - 7, ".tar.gz")))); |
143 | 656 | } |
144 | | |
145 | | /************************************************************************/ |
146 | | /* VSITarReader() */ |
147 | | /************************************************************************/ |
148 | | |
149 | | // TODO(schwehr): What is this ***NEWFILE*** thing? |
150 | | // And make it a symbolic constant. |
151 | | |
152 | | VSITarReader::VSITarReader(const char *pszTarFileName) |
153 | 656 | : fp(VSIFOpenL(pszTarFileName, "rb")) |
154 | 656 | { |
155 | 656 | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
156 | 656 | if (fp != nullptr) |
157 | 480 | { |
158 | 480 | GByte abySignature[24] = {}; |
159 | 480 | m_bIsFuzzerFriendly = |
160 | 480 | (VSIFReadL(abySignature, 1, 24, fp) == 24) && |
161 | 480 | (memcmp(abySignature, "FUZZER_FRIENDLY_ARCHIVE\n", 24) == 0 || |
162 | 0 | memcmp(abySignature, "***NEWFILE***:", strlen("***NEWFILE***:")) == |
163 | 0 | 0); |
164 | 480 | CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_SET)); |
165 | 480 | } |
166 | 656 | #endif |
167 | 656 | } |
168 | | |
169 | | /************************************************************************/ |
170 | | /* ~VSITarReader() */ |
171 | | /************************************************************************/ |
172 | | |
173 | | VSITarReader::~VSITarReader() |
174 | 656 | { |
175 | 656 | if (fp) |
176 | 480 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
177 | 656 | } |
178 | | |
179 | | /************************************************************************/ |
180 | | /* GetFileOffset() */ |
181 | | /************************************************************************/ |
182 | | |
183 | | VSIArchiveEntryFileOffset *VSITarReader::GetFileOffset() |
184 | 0 | { |
185 | 0 | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
186 | 0 | if (m_bIsFuzzerFriendly) |
187 | 0 | { |
188 | 0 | return new VSITarEntryFileOffset(nCurOffset, nNextFileSize, |
189 | 0 | osNextFileName); |
190 | 0 | } |
191 | 0 | #endif |
192 | 0 | return new VSITarEntryFileOffset(nCurOffset); |
193 | 0 | } |
194 | | |
195 | | /************************************************************************/ |
196 | | /* IsNumericFieldTerminator() */ |
197 | | /************************************************************************/ |
198 | | |
199 | | static bool IsNumericFieldTerminator(GByte byVal) |
200 | 0 | { |
201 | | // See https://github.com/Keruspe/tar-parser.rs/blob/master/tar.specs#L202 |
202 | 0 | return byVal == '\0' || byVal == ' '; |
203 | 0 | } |
204 | | |
205 | | /************************************************************************/ |
206 | | /* GotoNextFile() */ |
207 | | /************************************************************************/ |
208 | | |
209 | | int VSITarReader::GotoNextFile() |
210 | 480 | { |
211 | 480 | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
212 | 480 | if (m_bIsFuzzerFriendly) |
213 | 0 | { |
214 | 0 | const int nNewFileMarkerSize = |
215 | 0 | static_cast<int>(strlen("***NEWFILE***:")); |
216 | 0 | while (true) |
217 | 0 | { |
218 | 0 | if (m_abyBufferIdx >= m_abyBufferSize) |
219 | 0 | { |
220 | 0 | if (m_abyBufferSize == 0) |
221 | 0 | { |
222 | 0 | m_abyBufferSize = static_cast<int>( |
223 | 0 | VSIFReadL(m_abyBuffer, 1, BUFFER_SIZE, fp)); |
224 | 0 | if (m_abyBufferSize == 0) |
225 | 0 | return FALSE; |
226 | 0 | m_abyBuffer[m_abyBufferSize] = '\0'; |
227 | 0 | } |
228 | 0 | else |
229 | 0 | { |
230 | 0 | if (m_abyBufferSize < BUFFER_SIZE) |
231 | 0 | { |
232 | 0 | if (nCurOffset > 0 && nCurOffset != m_nCurOffsetOld) |
233 | 0 | { |
234 | 0 | nNextFileSize = VSIFTellL(fp); |
235 | 0 | if (nNextFileSize >= nCurOffset) |
236 | 0 | { |
237 | 0 | nNextFileSize -= nCurOffset; |
238 | 0 | m_nCurOffsetOld = nCurOffset; |
239 | 0 | return TRUE; |
240 | 0 | } |
241 | 0 | } |
242 | 0 | return FALSE; |
243 | 0 | } |
244 | 0 | memcpy(m_abyBuffer, m_abyBuffer + HALF_BUFFER_SIZE, |
245 | 0 | HALF_BUFFER_SIZE); |
246 | 0 | m_abyBufferSize = static_cast<int>( |
247 | 0 | VSIFReadL(m_abyBuffer + HALF_BUFFER_SIZE, 1, |
248 | 0 | HALF_BUFFER_SIZE, fp)); |
249 | 0 | if (m_abyBufferSize == 0) |
250 | 0 | return FALSE; |
251 | 0 | m_abyBufferIdx = 0; |
252 | 0 | m_abyBufferSize += HALF_BUFFER_SIZE; |
253 | 0 | m_abyBuffer[m_abyBufferSize] = '\0'; |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | 0 | std::string_view abyBuffer(reinterpret_cast<char *>(m_abyBuffer), |
258 | 0 | m_abyBufferSize); |
259 | 0 | const auto posNewFile = abyBuffer.find( |
260 | 0 | std::string_view("***NEWFILE***:", nNewFileMarkerSize), |
261 | 0 | m_abyBufferIdx); |
262 | 0 | if (posNewFile == std::string::npos) |
263 | 0 | { |
264 | 0 | m_abyBufferIdx = m_abyBufferSize; |
265 | 0 | } |
266 | 0 | else |
267 | 0 | { |
268 | 0 | m_abyBufferIdx = static_cast<int>(posNewFile); |
269 | | // 2: space for at least one-char filename and '\n' |
270 | 0 | if (m_abyBufferIdx < m_abyBufferSize - (nNewFileMarkerSize + 2)) |
271 | 0 | { |
272 | 0 | if (nCurOffset > 0 && nCurOffset != m_nCurOffsetOld) |
273 | 0 | { |
274 | 0 | nNextFileSize = VSIFTellL(fp); |
275 | 0 | nNextFileSize -= m_abyBufferSize; |
276 | 0 | nNextFileSize += m_abyBufferIdx; |
277 | 0 | if (nNextFileSize >= nCurOffset) |
278 | 0 | { |
279 | 0 | nNextFileSize -= nCurOffset; |
280 | 0 | m_nCurOffsetOld = nCurOffset; |
281 | 0 | return TRUE; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | m_abyBufferIdx += nNewFileMarkerSize; |
285 | 0 | const int nFilenameStartIdx = m_abyBufferIdx; |
286 | 0 | for (; m_abyBufferIdx < m_abyBufferSize && |
287 | 0 | m_abyBuffer[m_abyBufferIdx] != '\n'; |
288 | 0 | ++m_abyBufferIdx) |
289 | 0 | { |
290 | | // Do nothing. |
291 | 0 | } |
292 | 0 | if (m_abyBufferIdx < m_abyBufferSize) |
293 | 0 | { |
294 | 0 | osNextFileName.assign( |
295 | 0 | reinterpret_cast<const char *>(m_abyBuffer + |
296 | 0 | nFilenameStartIdx), |
297 | 0 | m_abyBufferIdx - nFilenameStartIdx); |
298 | 0 | nCurOffset = VSIFTellL(fp); |
299 | 0 | nCurOffset -= m_abyBufferSize; |
300 | 0 | nCurOffset += m_abyBufferIdx + 1; |
301 | 0 | } |
302 | 0 | } |
303 | 0 | else |
304 | 0 | { |
305 | 0 | m_abyBufferIdx = m_abyBufferSize; |
306 | 0 | } |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } |
310 | 480 | #endif |
311 | | |
312 | 480 | osNextFileName.clear(); |
313 | 480 | while (true) |
314 | 480 | { |
315 | 480 | GByte abyHeader[512] = {}; |
316 | 480 | if (VSIFReadL(abyHeader, 512, 1, fp) != 1) |
317 | 480 | return FALSE; |
318 | | |
319 | 0 | if (!(abyHeader[100] == 0x80 || |
320 | 0 | IsNumericFieldTerminator( |
321 | 0 | abyHeader[107])) || /* start/end of filemode */ |
322 | 0 | !(abyHeader[108] == 0x80 || |
323 | 0 | IsNumericFieldTerminator( |
324 | 0 | abyHeader[115])) || /* start/end of owner ID */ |
325 | 0 | !(abyHeader[116] == 0x80 || |
326 | 0 | IsNumericFieldTerminator( |
327 | 0 | abyHeader[123])) || /* start/end of group ID */ |
328 | 0 | !IsNumericFieldTerminator(abyHeader[135]) || /* end of file size */ |
329 | 0 | !IsNumericFieldTerminator(abyHeader[147])) /* end of mtime */ |
330 | 0 | { |
331 | 0 | return FALSE; |
332 | 0 | } |
333 | 0 | if (!(abyHeader[124] == ' ' || |
334 | 0 | (abyHeader[124] >= '0' && abyHeader[124] <= '7'))) |
335 | 0 | return FALSE; |
336 | | |
337 | 0 | if (osNextFileName.empty()) |
338 | 0 | { |
339 | 0 | osNextFileName.assign( |
340 | 0 | reinterpret_cast<const char *>(abyHeader), |
341 | 0 | CPLStrnlen(reinterpret_cast<const char *>(abyHeader), 100)); |
342 | 0 | } |
343 | |
|
344 | 0 | nNextFileSize = 0; |
345 | 0 | for (int i = 0; i < 11; i++) |
346 | 0 | { |
347 | 0 | if (abyHeader[124 + i] != ' ') |
348 | 0 | { |
349 | 0 | if (nNextFileSize > static_cast<GUIntBig>(GINTBIG_MAX / 8) || |
350 | 0 | abyHeader[124 + i] < '0' || abyHeader[124 + i] >= '8') |
351 | 0 | { |
352 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
353 | 0 | "Invalid file size for %s", |
354 | 0 | osNextFileName.c_str()); |
355 | 0 | return FALSE; |
356 | 0 | } |
357 | 0 | nNextFileSize = nNextFileSize * 8 + (abyHeader[124 + i] - '0'); |
358 | 0 | } |
359 | 0 | } |
360 | 0 | if (nNextFileSize > GINTBIG_MAX) |
361 | 0 | { |
362 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid file size for %s", |
363 | 0 | osNextFileName.c_str()); |
364 | 0 | return FALSE; |
365 | 0 | } |
366 | | |
367 | 0 | nModifiedTime = 0; |
368 | 0 | for (int i = 0; i < 11; i++) |
369 | 0 | { |
370 | 0 | if (abyHeader[136 + i] != ' ') |
371 | 0 | { |
372 | 0 | if (nModifiedTime > GINTBIG_MAX / 8 || |
373 | 0 | abyHeader[136 + i] < '0' || abyHeader[136 + i] >= '8' || |
374 | 0 | nModifiedTime * 8 > |
375 | 0 | GINTBIG_MAX - (abyHeader[136 + i] - '0')) |
376 | 0 | { |
377 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
378 | 0 | "Invalid mtime for %s", osNextFileName.c_str()); |
379 | 0 | return FALSE; |
380 | 0 | } |
381 | 0 | nModifiedTime = nModifiedTime * 8 + (abyHeader[136 + i] - '0'); |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | 0 | if (abyHeader[156] == 'L' && nNextFileSize > 0 && nNextFileSize < 32768) |
386 | 0 | { |
387 | | // If this is a large filename record, then read the filename |
388 | 0 | osNextFileName.clear(); |
389 | 0 | osNextFileName.resize( |
390 | 0 | static_cast<size_t>(((nNextFileSize + 511) / 512) * 512)); |
391 | 0 | if (VSIFReadL(&osNextFileName[0], osNextFileName.size(), 1, fp) != |
392 | 0 | 1) |
393 | 0 | return FALSE; |
394 | 0 | osNextFileName.resize(static_cast<size_t>(nNextFileSize)); |
395 | 0 | if (osNextFileName.back() == '\0') |
396 | 0 | osNextFileName.pop_back(); |
397 | 0 | } |
398 | 0 | else |
399 | 0 | { |
400 | | // Is it a ustar extension ? |
401 | | // Cf https://en.wikipedia.org/wiki/Tar_(computing)#UStar_format |
402 | 0 | if (memcmp(abyHeader + 257, "ustar\0", 6) == 0 && |
403 | 0 | abyHeader[345] != '\0') |
404 | 0 | { |
405 | 0 | std::string osFilenamePrefix; |
406 | 0 | osFilenamePrefix.assign( |
407 | 0 | reinterpret_cast<const char *>(abyHeader + 345), |
408 | 0 | CPLStrnlen(reinterpret_cast<const char *>(abyHeader + 345), |
409 | 0 | 155)); |
410 | 0 | osNextFileName = osFilenamePrefix + '/' + osNextFileName; |
411 | 0 | } |
412 | |
|
413 | 0 | break; |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | 0 | nCurOffset = VSIFTellL(fp); |
418 | |
|
419 | 0 | const GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512; |
420 | 0 | if (nBytesToSkip > (~(static_cast<GUIntBig>(0))) - nCurOffset) |
421 | 0 | { |
422 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Bad .tar structure"); |
423 | 0 | return FALSE; |
424 | 0 | } |
425 | | |
426 | 0 | if (VSIFSeekL(fp, nBytesToSkip, SEEK_CUR) < 0) |
427 | 0 | return FALSE; |
428 | | |
429 | 0 | return TRUE; |
430 | 0 | } |
431 | | |
432 | | /************************************************************************/ |
433 | | /* GotoFirstFile() */ |
434 | | /************************************************************************/ |
435 | | |
436 | | int VSITarReader::GotoFirstFile() |
437 | 480 | { |
438 | 480 | if (VSIFSeekL(fp, 0, SEEK_SET) < 0) |
439 | 0 | return FALSE; |
440 | 480 | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
441 | 480 | m_abyBufferIdx = 0; |
442 | 480 | m_abyBufferSize = 0; |
443 | 480 | nCurOffset = 0; |
444 | 480 | m_nCurOffsetOld = 0; |
445 | 480 | osNextFileName = ""; |
446 | 480 | nNextFileSize = 0; |
447 | 480 | #endif |
448 | 480 | return GotoNextFile(); |
449 | 480 | } |
450 | | |
451 | | /************************************************************************/ |
452 | | /* GotoFileOffset() */ |
453 | | /************************************************************************/ |
454 | | |
455 | | int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) |
456 | 0 | { |
457 | 0 | VSITarEntryFileOffset *pTarEntryOffset = |
458 | 0 | static_cast<VSITarEntryFileOffset *>(pOffset); |
459 | 0 | #ifdef HAVE_FUZZER_FRIENDLY_ARCHIVE |
460 | 0 | if (m_bIsFuzzerFriendly) |
461 | 0 | { |
462 | 0 | if (VSIFSeekL(fp, |
463 | 0 | pTarEntryOffset->m_nOffset + pTarEntryOffset->m_nFileSize, |
464 | 0 | SEEK_SET) < 0) |
465 | 0 | return FALSE; |
466 | 0 | m_abyBufferIdx = 0; |
467 | 0 | m_abyBufferSize = 0; |
468 | 0 | nCurOffset = pTarEntryOffset->m_nOffset; |
469 | 0 | m_nCurOffsetOld = pTarEntryOffset->m_nOffset; |
470 | 0 | osNextFileName = pTarEntryOffset->m_osFileName; |
471 | 0 | nNextFileSize = pTarEntryOffset->m_nFileSize; |
472 | 0 | return TRUE; |
473 | 0 | } |
474 | 0 | #endif |
475 | 0 | if (pTarEntryOffset->m_nOffset < 512 || |
476 | 0 | VSIFSeekL(fp, pTarEntryOffset->m_nOffset - 512, SEEK_SET) < 0) |
477 | 0 | return FALSE; |
478 | 0 | return GotoNextFile(); |
479 | 0 | } |
480 | | |
481 | | /************************************************************************/ |
482 | | /* ==================================================================== */ |
483 | | /* VSITarFilesystemHandler */ |
484 | | /* ==================================================================== */ |
485 | | /************************************************************************/ |
486 | | |
487 | | class VSITarFilesystemHandler final : public VSIArchiveFilesystemHandler |
488 | | { |
489 | | public: |
490 | | const char *GetPrefix() const override |
491 | 24.7k | { |
492 | 24.7k | return "/vsitar"; |
493 | 24.7k | } |
494 | | |
495 | | std::vector<CPLString> GetExtensions() const override; |
496 | | std::unique_ptr<VSIArchiveReader> |
497 | | CreateReader(const char *pszTarFileName) override; |
498 | | |
499 | | VSIVirtualHandleUniquePtr Open(const char *pszFilename, |
500 | | const char *pszAccess, bool bSetError, |
501 | | CSLConstList /* papszOptions */) override; |
502 | | }; |
503 | | |
504 | | /************************************************************************/ |
505 | | /* GetExtensions() */ |
506 | | /************************************************************************/ |
507 | | |
508 | | std::vector<CPLString> VSITarFilesystemHandler::GetExtensions() const |
509 | 4.99k | { |
510 | 4.99k | std::vector<CPLString> oList; |
511 | 4.99k | oList.push_back(".tar.gz"); |
512 | 4.99k | oList.push_back(".tar"); |
513 | 4.99k | oList.push_back(".tgz"); |
514 | 4.99k | return oList; |
515 | 4.99k | } |
516 | | |
517 | | /************************************************************************/ |
518 | | /* CreateReader() */ |
519 | | /************************************************************************/ |
520 | | |
521 | | std::unique_ptr<VSIArchiveReader> |
522 | | VSITarFilesystemHandler::CreateReader(const char *pszTarFileName) |
523 | 656 | { |
524 | 656 | CPLString osTarInFileName; |
525 | | |
526 | 656 | if (VSIIsTGZ(pszTarFileName)) |
527 | 1 | { |
528 | 1 | osTarInFileName = "/vsigzip/"; |
529 | 1 | osTarInFileName += pszTarFileName; |
530 | 1 | } |
531 | 655 | else |
532 | 655 | osTarInFileName = pszTarFileName; |
533 | | |
534 | 656 | auto poReader = std::make_unique<VSITarReader>(osTarInFileName); |
535 | 656 | if (!poReader->IsValid() || !poReader->GotoFirstFile()) |
536 | 656 | { |
537 | 656 | return nullptr; |
538 | 656 | } |
539 | | |
540 | 0 | return poReader; |
541 | 656 | } |
542 | | |
543 | | /************************************************************************/ |
544 | | /* Open() */ |
545 | | /************************************************************************/ |
546 | | |
547 | | VSIVirtualHandleUniquePtr |
548 | | VSITarFilesystemHandler::Open(const char *pszFilename, const char *pszAccess, |
549 | | bool bSetError, CSLConstList /* papszOptions */) |
550 | 1.30k | { |
551 | | |
552 | 1.30k | if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr) |
553 | 0 | { |
554 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
555 | 0 | "Only read-only mode is supported for /vsitar"); |
556 | 0 | return nullptr; |
557 | 0 | } |
558 | | |
559 | 1.30k | CPLString osTarInFileName; |
560 | 1.30k | std::unique_ptr<char, VSIFreeReleaser> tarFilename( |
561 | 1.30k | SplitFilename(pszFilename, osTarInFileName, true, bSetError)); |
562 | 1.30k | if (tarFilename == nullptr) |
563 | 1.15k | return nullptr; |
564 | | |
565 | 147 | auto poReader = OpenArchiveFile(tarFilename.get(), osTarInFileName); |
566 | 147 | if (poReader == nullptr) |
567 | 147 | { |
568 | 147 | return nullptr; |
569 | 147 | } |
570 | | |
571 | 0 | CPLString osSubFileName("/vsisubfile/"); |
572 | 0 | VSITarEntryFileOffset *pOffset = |
573 | 0 | reinterpret_cast<VSITarEntryFileOffset *>(poReader->GetFileOffset()); |
574 | 0 | osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->m_nOffset); |
575 | 0 | osSubFileName += "_"; |
576 | 0 | osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize()); |
577 | 0 | osSubFileName += ","; |
578 | 0 | delete pOffset; |
579 | |
|
580 | 0 | if (VSIIsTGZ(tarFilename.get())) |
581 | 0 | { |
582 | 0 | osSubFileName += "/vsigzip/"; |
583 | 0 | osSubFileName += tarFilename.get(); |
584 | 0 | } |
585 | 0 | else |
586 | 0 | osSubFileName += tarFilename.get(); |
587 | |
|
588 | 0 | return VSIFilesystemHandler::OpenStatic(osSubFileName, "rb"); |
589 | 147 | } |
590 | | |
591 | | //! @endcond |
592 | | |
593 | | /************************************************************************/ |
594 | | /* VSIInstallTarFileHandler() */ |
595 | | /************************************************************************/ |
596 | | |
597 | | /*! |
598 | | \brief Install /vsitar/ file system handler. |
599 | | |
600 | | A special file handler is installed that allows reading on-the-fly in TAR |
601 | | (regular .tar, or compressed .tar.gz/.tgz) archives. |
602 | | |
603 | | All portions of the file system underneath the base path "/vsitar/" will be |
604 | | handled by this driver. |
605 | | |
606 | | \verbatim embed:rst |
607 | | See :ref:`/vsitar/ documentation <vsitar>` |
608 | | \endverbatim |
609 | | |
610 | | @since GDAL 1.8.0 |
611 | | */ |
612 | | |
613 | | void VSIInstallTarFileHandler(void) |
614 | 3 | { |
615 | 3 | VSIFileManager::InstallHandler("/vsitar/", new VSITarFilesystemHandler()); |
616 | 3 | } |