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