Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for /vsi7z/ and /vsirar/
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_vsi_virtual.h"
15 :
16 : #ifndef HAVE_LIBARCHIVE
17 :
18 : /************************************************************************/
19 : /* VSIInstall7zFileHandler() */
20 : /************************************************************************/
21 :
22 : /*!
23 : \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
24 :
25 : \verbatim embed:rst
26 : See :ref:`/vsi7z/ documentation <vsi7z>`
27 : \endverbatim
28 :
29 : @since GDAL 3.7
30 : */
31 0 : void VSIInstall7zFileHandler(void)
32 : {
33 : // dummy
34 0 : }
35 :
36 : /************************************************************************/
37 : /* VSIInstallRarFileHandler() */
38 : /************************************************************************/
39 :
40 : /*!
41 : \brief Install /vsirar/ RAR file system handler (requires libarchive)
42 :
43 : \verbatim embed:rst
44 : See :ref:`/vsirar/ documentation <vsirar>`
45 : \endverbatim
46 :
47 : @since GDAL 3.7
48 : */
49 0 : void VSIInstallRarFileHandler(void)
50 : {
51 : // dummy
52 0 : }
53 :
54 : #else
55 :
56 : //! @cond Doxygen_Suppress
57 :
58 : #include <algorithm>
59 : #include <limits>
60 : #include <memory>
61 :
62 : // libarchive
63 : #ifdef USE_INTERNAL_LIBARCHIVE
64 : #include "archive_gdal_config.h"
65 : #endif
66 :
67 : #include "archive.h"
68 : #include "archive_entry.h"
69 :
70 : /************************************************************************/
71 : /* ==================================================================== */
72 : /* VSILibArchiveClientData */
73 : /* ==================================================================== */
74 : /************************************************************************/
75 :
76 : struct VSILibArchiveClientData
77 : {
78 : CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveClientData)
79 :
80 : const std::string m_osFilename;
81 : VSIVirtualHandle *m_poBaseHandle = nullptr;
82 : std::vector<GByte> m_abyBuffer{};
83 :
84 : VSILibArchiveClientData(const char *pszFilename) : m_osFilename(pszFilename)
85 : {
86 : m_abyBuffer.resize(4096);
87 : }
88 :
89 : static int openCbk(struct archive *pArchive, void *pClientData)
90 : {
91 : auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
92 : CPLDebug("VSIARCH", "Opening %s", poClientData->m_osFilename.c_str());
93 : poClientData->m_poBaseHandle = reinterpret_cast<VSIVirtualHandle *>(
94 : VSIFOpenL(poClientData->m_osFilename.c_str(), "rb"));
95 : if (poClientData->m_poBaseHandle == nullptr)
96 : {
97 : archive_set_error(pArchive, -1, "Cannot open file");
98 : return ARCHIVE_FATAL;
99 : }
100 : return ARCHIVE_OK;
101 : }
102 :
103 : static int closeCbk(struct archive *pArchive, void *pClientData)
104 : {
105 : auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
106 : int ret = 0;
107 : if (poClientData->m_poBaseHandle)
108 : {
109 : ret = poClientData->m_poBaseHandle->Close();
110 : delete poClientData->m_poBaseHandle;
111 : }
112 : delete poClientData;
113 : if (ret == 0)
114 : return ARCHIVE_OK;
115 : archive_set_error(pArchive, -1, "Cannot close file");
116 : return ARCHIVE_FATAL;
117 : }
118 :
119 : static la_ssize_t readCbk(struct archive *, void *pClientData,
120 : const void **ppBuffer)
121 : {
122 : auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
123 : *ppBuffer = poClientData->m_abyBuffer.data();
124 : return static_cast<la_ssize_t>(poClientData->m_poBaseHandle->Read(
125 : poClientData->m_abyBuffer.data(), 1,
126 : poClientData->m_abyBuffer.size()));
127 : }
128 :
129 : static la_int64_t seekCkb(struct archive *, void *pClientData,
130 : la_int64_t offset, int whence)
131 : {
132 : auto poClientData = static_cast<VSILibArchiveClientData *>(pClientData);
133 : if (whence == SEEK_CUR && offset < 0)
134 : {
135 : whence = SEEK_SET;
136 : offset = poClientData->m_poBaseHandle->Tell() + offset;
137 : }
138 : if (poClientData->m_poBaseHandle->Seek(
139 : static_cast<vsi_l_offset>(offset), whence) != 0)
140 : return ARCHIVE_FATAL;
141 : return static_cast<la_int64_t>(poClientData->m_poBaseHandle->Tell());
142 : }
143 : };
144 :
145 : /************************************************************************/
146 : /* VSILibArchiveReadOpen() */
147 : /************************************************************************/
148 :
149 : /**Open an archive, with the base handle being a VSIVirtualHandle* */
150 : static int VSILibArchiveReadOpen(struct archive *pArchive,
151 : const char *pszFilename)
152 : {
153 : archive_read_set_seek_callback(pArchive, VSILibArchiveClientData::seekCkb);
154 : return archive_read_open(pArchive, new VSILibArchiveClientData(pszFilename),
155 : VSILibArchiveClientData::openCbk,
156 : VSILibArchiveClientData::readCbk,
157 : VSILibArchiveClientData::closeCbk);
158 : }
159 :
160 : /************************************************************************/
161 : /* VSICreateArchiveHandle() */
162 : /************************************************************************/
163 :
164 : static struct archive *VSICreateArchiveHandle(const std::string &osFSPrefix)
165 : {
166 : auto pArchive = archive_read_new();
167 :
168 : if (osFSPrefix == "/vsi7z")
169 : {
170 : archive_read_support_format_7zip(pArchive);
171 : }
172 : else
173 : {
174 : archive_read_support_format_rar(pArchive);
175 : #ifdef ARCHIVE_FORMAT_RAR_V5
176 : archive_read_support_format_rar5(pArchive);
177 : #endif
178 : }
179 :
180 : return pArchive;
181 : }
182 :
183 : /************************************************************************/
184 : /* ==================================================================== */
185 : /* VSILibArchiveReader */
186 : /* ==================================================================== */
187 : /************************************************************************/
188 :
189 : class VSILibArchiveReader final : public VSIArchiveReader
190 : {
191 : CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveReader)
192 :
193 : std::string m_osArchiveFileName;
194 : struct archive *m_pArchive;
195 : std::string m_osPrefix;
196 : bool m_bFirst = true;
197 : std::string m_osFilename{};
198 : GUIntBig m_nFilesize = 0;
199 : GIntBig m_nMTime = 0;
200 :
201 : public:
202 : VSILibArchiveReader(const char *pszArchiveFileName,
203 : struct archive *pArchive, const std::string &osPrefix)
204 : : m_osArchiveFileName(pszArchiveFileName), m_pArchive(pArchive),
205 : m_osPrefix(osPrefix)
206 : {
207 : }
208 :
209 : ~VSILibArchiveReader() override;
210 :
211 : struct archive *GetArchiveHandler()
212 : {
213 : return m_pArchive;
214 : }
215 :
216 : int GotoFirstFileForced()
217 : {
218 : m_bFirst = false;
219 : return GotoFirstFile();
220 : }
221 :
222 : int GotoFirstFile() override;
223 : int GotoNextFile() override;
224 : VSIArchiveEntryFileOffset *GetFileOffset() override;
225 :
226 : GUIntBig GetFileSize() override
227 : {
228 : return m_nFilesize;
229 : }
230 :
231 : CPLString GetFileName() override
232 : {
233 : return m_osFilename;
234 : }
235 :
236 : GIntBig GetModifiedTime() override
237 : {
238 : return m_nMTime;
239 : }
240 :
241 : int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
242 :
243 : int GotoFileOffsetForced(VSIArchiveEntryFileOffset *pOffset);
244 : };
245 :
246 : /************************************************************************/
247 : /* ~VSILibArchiveReader() */
248 : /************************************************************************/
249 :
250 : VSILibArchiveReader::~VSILibArchiveReader()
251 : {
252 : archive_free(m_pArchive);
253 : }
254 :
255 : /************************************************************************/
256 : /* GotoFirstFile() */
257 : /************************************************************************/
258 :
259 : int VSILibArchiveReader::GotoFirstFile()
260 : {
261 : if (!m_bFirst)
262 : {
263 : archive_free(m_pArchive);
264 :
265 : m_pArchive = VSICreateArchiveHandle(m_osPrefix);
266 :
267 : if (VSILibArchiveReadOpen(m_pArchive, m_osArchiveFileName.c_str()))
268 : {
269 : CPLDebug("VSIARCH", "%s: %s", m_osArchiveFileName.c_str(),
270 : archive_error_string(m_pArchive));
271 : return false;
272 : }
273 : m_bFirst = true;
274 : }
275 : return GotoNextFile();
276 : }
277 :
278 : /************************************************************************/
279 : /* GotoNextFile() */
280 : /************************************************************************/
281 :
282 : int VSILibArchiveReader::GotoNextFile()
283 : {
284 : struct archive_entry *entry;
285 : int r = archive_read_next_header(m_pArchive, &entry);
286 : if (r == ARCHIVE_EOF)
287 : return FALSE;
288 : if (r != ARCHIVE_OK)
289 : {
290 : CPLDebug("VSIARCH", "%s", archive_error_string(m_pArchive));
291 : return FALSE;
292 : }
293 : m_osFilename = archive_entry_pathname_utf8(entry);
294 : m_nFilesize = archive_entry_size(entry);
295 : m_nMTime = archive_entry_mtime(entry);
296 : return TRUE;
297 : }
298 :
299 : /************************************************************************/
300 : /* VSILibArchiveEntryFileOffset */
301 : /************************************************************************/
302 :
303 : struct VSILibArchiveEntryFileOffset : public VSIArchiveEntryFileOffset
304 : {
305 : const std::string m_osFilename;
306 :
307 : VSILibArchiveEntryFileOffset(const std::string &osFilename)
308 : : m_osFilename(osFilename)
309 : {
310 : }
311 :
312 : ~VSILibArchiveEntryFileOffset() override;
313 : };
314 :
315 : VSILibArchiveEntryFileOffset::~VSILibArchiveEntryFileOffset() = default;
316 :
317 : /************************************************************************/
318 : /* GetFileOffset() */
319 : /************************************************************************/
320 :
321 : VSIArchiveEntryFileOffset *VSILibArchiveReader::GetFileOffset()
322 : {
323 : return new VSILibArchiveEntryFileOffset(m_osFilename);
324 : }
325 :
326 : /************************************************************************/
327 : /* GotoFileOffset() */
328 : /************************************************************************/
329 :
330 : int VSILibArchiveReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
331 : {
332 : VSILibArchiveEntryFileOffset *pMyOffset =
333 : static_cast<VSILibArchiveEntryFileOffset *>(pOffset);
334 : if (!GotoFirstFile())
335 : return false;
336 : while (m_osFilename != pMyOffset->m_osFilename)
337 : {
338 : if (!GotoNextFile())
339 : return false;
340 : }
341 : return true;
342 : }
343 :
344 : /************************************************************************/
345 : /* GotoFileOffsetForced() */
346 : /************************************************************************/
347 :
348 : int VSILibArchiveReader::GotoFileOffsetForced(
349 : VSIArchiveEntryFileOffset *pOffset)
350 : {
351 : m_bFirst = false;
352 : return GotoFileOffset(pOffset);
353 : }
354 :
355 : /************************************************************************/
356 : /* ==================================================================== */
357 : /* VSILibArchiveHandler */
358 : /* ==================================================================== */
359 : /************************************************************************/
360 :
361 : class VSILibArchiveHandler final : public VSIVirtualHandle
362 : {
363 : const std::string m_osFilename;
364 : std::unique_ptr<VSILibArchiveReader> m_poReader;
365 : std::unique_ptr<VSIArchiveEntryFileOffset> m_pOffset;
366 : vsi_l_offset m_nOffset = 0;
367 : bool m_bEOF = false;
368 : bool m_bError = false;
369 :
370 : public:
371 : VSILibArchiveHandler(const std::string &osFilename,
372 : VSILibArchiveReader *poReader)
373 : : m_osFilename(osFilename), m_poReader(poReader),
374 : m_pOffset(poReader->GetFileOffset())
375 : {
376 : }
377 :
378 : size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
379 : int Seek(vsi_l_offset nOffset, int nWhence) override;
380 :
381 : vsi_l_offset Tell() override
382 : {
383 : return m_nOffset;
384 : }
385 :
386 : size_t Write(const void *, size_t, size_t) override
387 : {
388 : return 0;
389 : }
390 :
391 : void ClearErr() override
392 : {
393 : m_bEOF = false;
394 : m_bError = false;
395 : }
396 :
397 : int Eof() override
398 : {
399 : return m_bEOF ? 1 : 0;
400 : }
401 :
402 : int Error() override
403 : {
404 : return m_bError ? 1 : 0;
405 : }
406 :
407 : int Close() override
408 : {
409 : m_poReader.reset();
410 : return 0;
411 : }
412 : };
413 :
414 : /************************************************************************/
415 : /* Read() */
416 : /************************************************************************/
417 :
418 : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
419 : {
420 : if (m_bError || nSize == 0 || nCount == 0)
421 : return 0;
422 : const auto nFileSize = m_poReader->GetFileSize();
423 : if (m_nOffset == nFileSize)
424 : {
425 : m_bEOF = true;
426 : return 0;
427 : }
428 : size_t nToRead = nSize * nCount;
429 : auto pArchive = m_poReader->GetArchiveHandler();
430 : auto nReadUnsigned = archive_read_data(pArchive, pBuffer, nToRead);
431 : if (nReadUnsigned < 0)
432 : {
433 : m_bError = true;
434 : CPLDebug("VSIARCH", "Read(): %s", archive_error_string(pArchive));
435 : return 0;
436 : }
437 : const auto nRead = static_cast<size_t>(nReadUnsigned);
438 : if (nRead < nToRead)
439 : {
440 : if (m_nOffset + nRead == nFileSize)
441 : m_bEOF = true;
442 : else
443 : m_bError = true;
444 : }
445 : m_nOffset += nRead;
446 : return nRead / nSize;
447 : }
448 :
449 : /************************************************************************/
450 : /* Seek() */
451 : /************************************************************************/
452 :
453 : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
454 : {
455 : if (m_bError)
456 : return -1;
457 : m_bEOF = false;
458 : if (nWhence == SEEK_END && nOffset == 0)
459 : {
460 : m_nOffset = m_poReader->GetFileSize();
461 : return 0;
462 : }
463 : auto nNewOffset = m_nOffset;
464 : if (nWhence == SEEK_CUR)
465 : nNewOffset += nOffset;
466 : else
467 : nNewOffset = nOffset;
468 : if (nNewOffset == m_nOffset)
469 : return 0;
470 :
471 : if (nNewOffset < m_nOffset)
472 : {
473 : CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
474 : // If we need to go backwards, we must completely reset the
475 : // reader!
476 : if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
477 : {
478 : m_bError = true;
479 : return -1;
480 : }
481 : m_nOffset = 0;
482 : }
483 :
484 : std::vector<GByte> abyBuffer(4096);
485 : while (m_nOffset < nNewOffset)
486 : {
487 : size_t nToRead = static_cast<size_t>(
488 : std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
489 : if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
490 : break;
491 : }
492 :
493 : return 0;
494 : }
495 :
496 : /************************************************************************/
497 : /* ==================================================================== */
498 : /* VSILibArchiveFilesystemHandler */
499 : /* ==================================================================== */
500 : /************************************************************************/
501 :
502 : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
503 : {
504 : CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
505 :
506 : const std::string m_osPrefix;
507 :
508 : const char *GetPrefix() const override
509 : {
510 : return m_osPrefix.c_str();
511 : }
512 :
513 : std::vector<CPLString> GetExtensions() const override
514 : {
515 : if (m_osPrefix == "/vsi7z")
516 : {
517 : return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
518 : }
519 : else
520 : {
521 : return {".rar"};
522 : }
523 : }
524 :
525 : virtual std::unique_ptr<VSIArchiveReader>
526 : CreateReader(const char *pszArchiveFileName) override;
527 :
528 : public:
529 : VSILibArchiveFilesystemHandler(const std::string &osPrefix)
530 : : m_osPrefix(osPrefix)
531 : {
532 : }
533 :
534 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
535 : const char *pszAccess, bool bSetError,
536 : CSLConstList papszOptions) override;
537 : };
538 :
539 : /************************************************************************/
540 : /* Open() */
541 : /************************************************************************/
542 :
543 : VSIVirtualHandleUniquePtr
544 : VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
545 : const char *pszAccess, bool bSetError,
546 : CSLConstList)
547 : {
548 : if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
549 : {
550 : CPLError(CE_Failure, CPLE_AppDefined,
551 : "Only read-only mode is supported for %s", m_osPrefix.c_str());
552 : return nullptr;
553 : }
554 :
555 : CPLString osFileInArchive;
556 : char *pszArchiveFileName =
557 : SplitFilename(pszFilename, osFileInArchive, true, bSetError);
558 : if (pszArchiveFileName == nullptr)
559 : return nullptr;
560 :
561 : auto poReader = std::unique_ptr<VSILibArchiveReader>(
562 : cpl::down_cast<VSILibArchiveReader *>(
563 : OpenArchiveFile(pszArchiveFileName, osFileInArchive).release()));
564 : CPLFree(pszArchiveFileName);
565 : if (poReader == nullptr)
566 : {
567 : return nullptr;
568 : }
569 :
570 : if (osFileInArchive.empty())
571 : poReader->GotoFirstFileForced();
572 :
573 : return VSIVirtualHandleUniquePtr(
574 : std::make_unique<VSILibArchiveHandler>(pszFilename, poReader.release())
575 : .release());
576 : }
577 :
578 : /************************************************************************/
579 : /* CreateReader() */
580 : /************************************************************************/
581 :
582 : std::unique_ptr<VSIArchiveReader>
583 : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
584 : {
585 : auto pArchive = VSICreateArchiveHandle(m_osPrefix);
586 :
587 : if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
588 : {
589 : CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
590 : archive_error_string(pArchive));
591 : archive_read_free(pArchive);
592 : return nullptr;
593 : }
594 : return std::make_unique<VSILibArchiveReader>(pszArchiveFileName, pArchive,
595 : m_osPrefix);
596 : }
597 :
598 : //! @endcond
599 :
600 : /************************************************************************/
601 : /* VSIInstall7zFileHandler() */
602 : /************************************************************************/
603 :
604 : /*!
605 : \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
606 :
607 : \verbatim embed:rst
608 : See :ref:`/vsi7z/ documentation <vsi7z>`
609 : \endverbatim
610 :
611 : @since GDAL 3.7
612 : */
613 : void VSIInstall7zFileHandler(void)
614 : {
615 : VSIFileManager::InstallHandler(
616 : "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
617 : }
618 :
619 : /************************************************************************/
620 : /* VSIInstallRarFileHandler() */
621 : /************************************************************************/
622 :
623 : /*!
624 : \brief Install /vsirar/ rar file system handler (requires libarchive)
625 :
626 : \verbatim embed:rst
627 : See :ref:`/vsirar/ documentation <vsirar>`
628 : \endverbatim
629 :
630 : @since GDAL 3.7
631 : */
632 : void VSIInstallRarFileHandler(void)
633 : {
634 : VSIFileManager::InstallHandler(
635 : "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
636 : }
637 :
638 : #endif
|