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