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