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 : virtual int GotoFirstFile() override;
219 : virtual int GotoNextFile() override;
220 : virtual VSIArchiveEntryFileOffset *GetFileOffset() override;
221 :
222 : virtual GUIntBig GetFileSize() override
223 : {
224 : return m_nFilesize;
225 : }
226 :
227 : virtual CPLString GetFileName() override
228 : {
229 : return m_osFilename;
230 : }
231 :
232 : virtual GIntBig GetModifiedTime() override
233 : {
234 : return m_nMTime;
235 : }
236 :
237 : virtual int GotoFileOffset(VSIArchiveEntryFileOffset *pOffset) override;
238 :
239 : int GotoFileOffsetForced(VSIArchiveEntryFileOffset *pOffset);
240 : };
241 :
242 : /************************************************************************/
243 : /* ~VSILibArchiveReader() */
244 : /************************************************************************/
245 :
246 : VSILibArchiveReader::~VSILibArchiveReader()
247 : {
248 : archive_free(m_pArchive);
249 : }
250 :
251 : /************************************************************************/
252 : /* GotoFirstFile() */
253 : /************************************************************************/
254 :
255 : int VSILibArchiveReader::GotoFirstFile()
256 : {
257 : if (!m_bFirst)
258 : {
259 : archive_free(m_pArchive);
260 :
261 : m_pArchive = VSICreateArchiveHandle(m_osPrefix);
262 :
263 : if (VSILibArchiveReadOpen(m_pArchive, m_osArchiveFileName.c_str()))
264 : {
265 : CPLDebug("VSIARCH", "%s: %s", m_osArchiveFileName.c_str(),
266 : archive_error_string(m_pArchive));
267 : return false;
268 : }
269 : m_bFirst = true;
270 : }
271 : return GotoNextFile();
272 : }
273 :
274 : /************************************************************************/
275 : /* GotoNextFile() */
276 : /************************************************************************/
277 :
278 : int VSILibArchiveReader::GotoNextFile()
279 : {
280 : struct archive_entry *entry;
281 : int r = archive_read_next_header(m_pArchive, &entry);
282 : if (r == ARCHIVE_EOF)
283 : return FALSE;
284 : if (r != ARCHIVE_OK)
285 : {
286 : CPLDebug("VSIARCH", "%s", archive_error_string(m_pArchive));
287 : return FALSE;
288 : }
289 : m_osFilename = archive_entry_pathname_utf8(entry);
290 : m_nFilesize = archive_entry_size(entry);
291 : m_nMTime = archive_entry_mtime(entry);
292 : return TRUE;
293 : }
294 :
295 : /************************************************************************/
296 : /* VSILibArchiveEntryFileOffset */
297 : /************************************************************************/
298 :
299 : struct VSILibArchiveEntryFileOffset : public VSIArchiveEntryFileOffset
300 : {
301 : const std::string m_osFilename;
302 :
303 : VSILibArchiveEntryFileOffset(const std::string &osFilename)
304 : : m_osFilename(osFilename)
305 : {
306 : }
307 : };
308 :
309 : /************************************************************************/
310 : /* GetFileOffset() */
311 : /************************************************************************/
312 :
313 : VSIArchiveEntryFileOffset *VSILibArchiveReader::GetFileOffset()
314 : {
315 : return new VSILibArchiveEntryFileOffset(m_osFilename);
316 : }
317 :
318 : /************************************************************************/
319 : /* GotoFileOffset() */
320 : /************************************************************************/
321 :
322 : int VSILibArchiveReader::GotoFileOffset(VSIArchiveEntryFileOffset *pOffset)
323 : {
324 : VSILibArchiveEntryFileOffset *pMyOffset =
325 : static_cast<VSILibArchiveEntryFileOffset *>(pOffset);
326 : if (!GotoFirstFile())
327 : return false;
328 : while (m_osFilename != pMyOffset->m_osFilename)
329 : {
330 : if (!GotoNextFile())
331 : return false;
332 : }
333 : return true;
334 : }
335 :
336 : /************************************************************************/
337 : /* GotoFileOffsetForced() */
338 : /************************************************************************/
339 :
340 : int VSILibArchiveReader::GotoFileOffsetForced(
341 : VSIArchiveEntryFileOffset *pOffset)
342 : {
343 : m_bFirst = false;
344 : return GotoFileOffset(pOffset);
345 : }
346 :
347 : /************************************************************************/
348 : /* ==================================================================== */
349 : /* VSILibArchiveHandler */
350 : /* ==================================================================== */
351 : /************************************************************************/
352 :
353 : class VSILibArchiveHandler final : public VSIVirtualHandle
354 : {
355 : const std::string m_osFilename;
356 : std::unique_ptr<VSILibArchiveReader> m_poReader;
357 : std::unique_ptr<VSIArchiveEntryFileOffset> m_pOffset;
358 : vsi_l_offset m_nOffset = 0;
359 : bool m_bEOF = false;
360 : bool m_bError = false;
361 :
362 : public:
363 : VSILibArchiveHandler(const std::string &osFilename,
364 : VSILibArchiveReader *poReader)
365 : : m_osFilename(osFilename), m_poReader(poReader),
366 : m_pOffset(poReader->GetFileOffset())
367 : {
368 : }
369 :
370 : virtual size_t Read(void *pBuffer, size_t nSize, size_t nCount) override;
371 : virtual int Seek(vsi_l_offset nOffset, int nWhence) override;
372 :
373 : virtual vsi_l_offset Tell() override
374 : {
375 : return m_nOffset;
376 : }
377 :
378 : virtual size_t Write(const void *, size_t, size_t) override
379 : {
380 : return 0;
381 : }
382 :
383 : virtual void ClearErr() override
384 : {
385 : m_bEOF = false;
386 : m_bError = false;
387 : }
388 :
389 : virtual int Eof() override
390 : {
391 : return m_bEOF ? 1 : 0;
392 : }
393 :
394 : virtual int Error() override
395 : {
396 : return m_bError ? 1 : 0;
397 : }
398 :
399 : virtual int Close() override
400 : {
401 : return 0;
402 : }
403 : };
404 :
405 : /************************************************************************/
406 : /* Read() */
407 : /************************************************************************/
408 :
409 : size_t VSILibArchiveHandler::Read(void *pBuffer, size_t nSize, size_t nCount)
410 : {
411 : if (m_bError || nSize == 0 || nCount == 0)
412 : return 0;
413 : if (m_nOffset == m_poReader->GetFileSize())
414 : {
415 : m_bEOF = true;
416 : return 0;
417 : }
418 : size_t nToRead = nSize * nCount;
419 : auto nRead = static_cast<size_t>(
420 : archive_read_data(m_poReader->GetArchiveHandler(), pBuffer, nToRead));
421 : if (nRead < nToRead)
422 : {
423 : if (m_nOffset + nRead == m_poReader->GetFileSize())
424 : m_bEOF = true;
425 : else
426 : m_bError = true;
427 : }
428 : m_nOffset += nRead;
429 : return nRead / nSize;
430 : }
431 :
432 : /************************************************************************/
433 : /* Seek() */
434 : /************************************************************************/
435 :
436 : int VSILibArchiveHandler::Seek(vsi_l_offset nOffset, int nWhence)
437 : {
438 : if (m_bError)
439 : return -1;
440 : m_bEOF = false;
441 : if (nWhence == SEEK_END && nOffset == 0)
442 : {
443 : m_nOffset = m_poReader->GetFileSize();
444 : return 0;
445 : }
446 : auto nNewOffset = m_nOffset;
447 : if (nWhence == SEEK_CUR)
448 : nNewOffset += nOffset;
449 : else
450 : nNewOffset = nOffset;
451 : if (nNewOffset == m_nOffset)
452 : return 0;
453 :
454 : if (nNewOffset < m_nOffset)
455 : {
456 : CPLDebug("VSIARCH", "Seeking backwards in %s", m_osFilename.c_str());
457 : // If we need to go backwards, we must completely reset the
458 : // reader!
459 : if (!m_poReader->GotoFileOffsetForced(m_pOffset.get()))
460 : {
461 : m_bError = true;
462 : return -1;
463 : }
464 : m_nOffset = 0;
465 : }
466 :
467 : std::vector<GByte> abyBuffer(4096);
468 : while (m_nOffset < nNewOffset)
469 : {
470 : size_t nToRead = static_cast<size_t>(
471 : std::min<vsi_l_offset>(abyBuffer.size(), nNewOffset - m_nOffset));
472 : if (Read(abyBuffer.data(), 1, nToRead) != nToRead)
473 : break;
474 : }
475 :
476 : return 0;
477 : }
478 :
479 : /************************************************************************/
480 : /* ==================================================================== */
481 : /* VSILibArchiveFilesystemHandler */
482 : /* ==================================================================== */
483 : /************************************************************************/
484 :
485 : class VSILibArchiveFilesystemHandler final : public VSIArchiveFilesystemHandler
486 : {
487 : CPL_DISALLOW_COPY_ASSIGN(VSILibArchiveFilesystemHandler)
488 :
489 : const std::string m_osPrefix;
490 :
491 : virtual const char *GetPrefix() override
492 : {
493 : return m_osPrefix.c_str();
494 : }
495 :
496 : virtual std::vector<CPLString> GetExtensions() override
497 : {
498 : if (m_osPrefix == "/vsi7z")
499 : {
500 : return {".7z", ".lpk", ".lpkx", ".mpk", ".mpkx", ".ppkx"};
501 : }
502 : else
503 : {
504 : return {".rar"};
505 : }
506 : }
507 :
508 : virtual VSIArchiveReader *
509 : CreateReader(const char *pszArchiveFileName) override;
510 :
511 : public:
512 : VSILibArchiveFilesystemHandler(const std::string &osPrefix)
513 : : m_osPrefix(osPrefix)
514 : {
515 : }
516 :
517 : virtual VSIVirtualHandle *Open(const char *pszFilename,
518 : const char *pszAccess, bool bSetError,
519 : CSLConstList papszOptions) override;
520 : };
521 :
522 : /************************************************************************/
523 : /* Open() */
524 : /************************************************************************/
525 :
526 : VSIVirtualHandle *VSILibArchiveFilesystemHandler::Open(const char *pszFilename,
527 : const char *pszAccess,
528 : bool, CSLConstList)
529 : {
530 : if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
531 : {
532 : CPLError(CE_Failure, CPLE_AppDefined,
533 : "Only read-only mode is supported for %s", m_osPrefix.c_str());
534 : return nullptr;
535 : }
536 :
537 : CPLString osFileInArchive;
538 : char *pszArchiveFileName =
539 : SplitFilename(pszFilename, osFileInArchive, TRUE);
540 : if (pszArchiveFileName == nullptr)
541 : return nullptr;
542 :
543 : VSILibArchiveReader *poReader = cpl::down_cast<VSILibArchiveReader *>(
544 : OpenArchiveFile(pszArchiveFileName, osFileInArchive));
545 : CPLFree(pszArchiveFileName);
546 : if (poReader == nullptr)
547 : {
548 : return nullptr;
549 : }
550 :
551 : return new VSILibArchiveHandler(pszFilename, poReader);
552 : }
553 :
554 : /************************************************************************/
555 : /* CreateReader() */
556 : /************************************************************************/
557 :
558 : VSIArchiveReader *
559 : VSILibArchiveFilesystemHandler::CreateReader(const char *pszArchiveFileName)
560 : {
561 : auto pArchive = VSICreateArchiveHandle(m_osPrefix);
562 :
563 : if (VSILibArchiveReadOpen(pArchive, pszArchiveFileName))
564 : {
565 : CPLDebug("VSIARCH", "%s: %s", pszArchiveFileName,
566 : archive_error_string(pArchive));
567 : archive_read_free(pArchive);
568 : return nullptr;
569 : }
570 : return new VSILibArchiveReader(pszArchiveFileName, pArchive, m_osPrefix);
571 : }
572 :
573 : //! @endcond
574 :
575 : /************************************************************************/
576 : /* VSIInstall7zFileHandler() */
577 : /************************************************************************/
578 :
579 : /*!
580 : \brief Install /vsi7z/ 7zip file system handler (requires libarchive)
581 :
582 : \verbatim embed:rst
583 : See :ref:`/vsi7z/ documentation <vsi7z>`
584 : \endverbatim
585 :
586 : @since GDAL 3.7
587 : */
588 : void VSIInstall7zFileHandler(void)
589 : {
590 : VSIFileManager::InstallHandler(
591 : "/vsi7z/", new VSILibArchiveFilesystemHandler("/vsi7z"));
592 : }
593 :
594 : /************************************************************************/
595 : /* VSIInstallRarFileHandler() */
596 : /************************************************************************/
597 :
598 : /*!
599 : \brief Install /vsirar/ rar file system handler (requires libarchive)
600 :
601 : \verbatim embed:rst
602 : See :ref:`/vsirar/ documentation <vsirar>`
603 : \endverbatim
604 :
605 : @since GDAL 3.7
606 : */
607 : void VSIInstallRarFileHandler(void)
608 : {
609 : VSIFileManager::InstallHandler(
610 : "/vsirar/", new VSILibArchiveFilesystemHandler("/vsirar"));
611 : }
612 :
613 : #endif
|