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