Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: VSI Virtual File System
4 : * Purpose: Implementation of subfile virtual IO functions.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_vsi.h"
16 :
17 : #include <cerrno>
18 : #include <cstddef>
19 : #include <cstring>
20 : #include <limits>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_multiproc.h"
24 : #include "cpl_string.h"
25 : #include "cpl_vsi_virtual.h"
26 :
27 : /************************************************************************/
28 : /* ==================================================================== */
29 : /* VSISubFileHandle */
30 : /* ==================================================================== */
31 : /************************************************************************/
32 :
33 : class VSISubFileHandle final : public VSIVirtualHandle
34 : {
35 : CPL_DISALLOW_COPY_ASSIGN(VSISubFileHandle)
36 :
37 : public:
38 : VSILFILE *fp = nullptr;
39 : vsi_l_offset nSubregionOffset = 0;
40 : vsi_l_offset nSubregionSize = 0;
41 : bool bAtEOF = false;
42 : bool bError = false;
43 :
44 232 : VSISubFileHandle() = default;
45 : ~VSISubFileHandle() override;
46 :
47 : int Seek(vsi_l_offset nOffset, int nWhence) override;
48 : vsi_l_offset Tell() override;
49 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
50 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
51 : void ClearErr() override;
52 : int Eof() override;
53 : int Error() override;
54 : int Close() override;
55 : };
56 :
57 : /************************************************************************/
58 : /* ==================================================================== */
59 : /* VSISubFileFilesystemHandler */
60 : /* ==================================================================== */
61 : /************************************************************************/
62 :
63 : class VSISubFileFilesystemHandler final : public VSIFilesystemHandler
64 : {
65 : CPL_DISALLOW_COPY_ASSIGN(VSISubFileFilesystemHandler)
66 :
67 : public:
68 1754 : VSISubFileFilesystemHandler() = default;
69 2242 : ~VSISubFileFilesystemHandler() override = default;
70 :
71 : static int DecomposePath(const char *pszPath, CPLString &osFilename,
72 : vsi_l_offset &nSubFileOffset,
73 : vsi_l_offset &nSubFileSize);
74 :
75 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
76 : const char *pszAccess, bool bSetError,
77 : CSLConstList /* papszOptions */) override;
78 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
79 : int nFlags) override;
80 : int Unlink(const char *pszFilename) override;
81 : int Mkdir(const char *pszDirname, long nMode) override;
82 : int Rmdir(const char *pszDirname) override;
83 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
84 : };
85 :
86 : /************************************************************************/
87 : /* ==================================================================== */
88 : /* VSISubFileHandle */
89 : /* ==================================================================== */
90 : /************************************************************************/
91 :
92 464 : VSISubFileHandle::~VSISubFileHandle()
93 : {
94 232 : VSISubFileHandle::Close();
95 464 : }
96 :
97 : /************************************************************************/
98 : /* Close() */
99 : /************************************************************************/
100 :
101 464 : int VSISubFileHandle::Close()
102 :
103 : {
104 464 : if (fp == nullptr)
105 232 : return -1;
106 232 : int nRet = VSIFCloseL(fp);
107 232 : fp = nullptr;
108 :
109 232 : return nRet;
110 : }
111 :
112 : /************************************************************************/
113 : /* Seek() */
114 : /************************************************************************/
115 :
116 1516 : int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
117 :
118 : {
119 1516 : bAtEOF = false;
120 :
121 1516 : if (nWhence == SEEK_SET)
122 : {
123 1398 : if (nOffset >
124 1398 : std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
125 0 : return -1;
126 1398 : nOffset += nSubregionOffset;
127 : }
128 118 : else if (nWhence == SEEK_CUR)
129 : {
130 : // handle normally.
131 : }
132 93 : else if (nWhence == SEEK_END)
133 : {
134 93 : if (nSubregionSize != 0)
135 : {
136 74 : nOffset = nSubregionOffset + nSubregionSize;
137 74 : nWhence = SEEK_SET;
138 : }
139 : }
140 : else
141 : {
142 0 : errno = EINVAL;
143 0 : return -1;
144 : }
145 :
146 1516 : return VSIFSeekL(fp, nOffset, nWhence);
147 : }
148 :
149 : /************************************************************************/
150 : /* Tell() */
151 : /************************************************************************/
152 :
153 3304 : vsi_l_offset VSISubFileHandle::Tell()
154 :
155 : {
156 3304 : vsi_l_offset nBasePos = VSIFTellL(fp);
157 3304 : if (nBasePos >= nSubregionOffset)
158 3304 : return nBasePos - nSubregionOffset;
159 0 : return 0;
160 : }
161 :
162 : /************************************************************************/
163 : /* Read() */
164 : /************************************************************************/
165 :
166 4059 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
167 :
168 : {
169 4059 : size_t nRet = 0;
170 4059 : if (nSubregionSize == 0)
171 : {
172 632 : nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
173 : }
174 : else
175 : {
176 3427 : if (nSize == 0)
177 0 : return 0;
178 :
179 3427 : const vsi_l_offset nCurOffset = VSIFTellL(fp);
180 3427 : if (nCurOffset >= nSubregionOffset + nSubregionSize)
181 : {
182 5 : bAtEOF = true;
183 5 : return 0;
184 : }
185 :
186 3422 : const size_t nByteToRead = nSize * nCount;
187 3422 : if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize)
188 : {
189 : const int nRead = static_cast<int>(
190 182 : VSIFReadL(pBuffer, 1,
191 91 : static_cast<size_t>(nSubregionOffset +
192 91 : nSubregionSize - nCurOffset),
193 91 : fp));
194 91 : nRet = nRead / nSize;
195 : }
196 : else
197 : {
198 3331 : nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
199 : }
200 : }
201 :
202 4054 : if (nRet < nCount)
203 : {
204 216 : if (fp->Eof())
205 125 : bAtEOF = true;
206 : else /* if (fp->Error()) */
207 91 : bError = true;
208 : }
209 :
210 4054 : return nRet;
211 : }
212 :
213 : /************************************************************************/
214 : /* Write() */
215 : /************************************************************************/
216 :
217 2618 : size_t VSISubFileHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
218 :
219 : {
220 2618 : bAtEOF = false;
221 :
222 2618 : if (nSubregionSize == 0)
223 2618 : return VSIFWriteL(pBuffer, nSize, nCount, fp);
224 :
225 0 : if (nSize == 0)
226 0 : return 0;
227 :
228 0 : const vsi_l_offset nCurOffset = VSIFTellL(fp);
229 0 : if (nCurOffset >= nSubregionOffset + nSubregionSize)
230 0 : return 0;
231 :
232 0 : const size_t nByteToWrite = nSize * nCount;
233 0 : if (nCurOffset + nByteToWrite > nSubregionOffset + nSubregionSize)
234 : {
235 0 : const int nWritten = static_cast<int>(VSIFWriteL(
236 : pBuffer, 1,
237 0 : static_cast<size_t>(nSubregionOffset + nSubregionSize - nCurOffset),
238 0 : fp));
239 0 : return nWritten / nSize;
240 : }
241 :
242 0 : return VSIFWriteL(pBuffer, nSize, nCount, fp);
243 : }
244 :
245 : /************************************************************************/
246 : /* ClearErr() */
247 : /************************************************************************/
248 :
249 2 : void VSISubFileHandle::ClearErr()
250 :
251 : {
252 2 : fp->ClearErr();
253 2 : bAtEOF = false;
254 2 : bError = false;
255 2 : }
256 :
257 : /************************************************************************/
258 : /* Error() */
259 : /************************************************************************/
260 :
261 22 : int VSISubFileHandle::Error()
262 :
263 : {
264 22 : return bError;
265 : }
266 :
267 : /************************************************************************/
268 : /* Eof() */
269 : /************************************************************************/
270 :
271 26 : int VSISubFileHandle::Eof()
272 :
273 : {
274 26 : return bAtEOF;
275 : }
276 :
277 : /************************************************************************/
278 : /* ==================================================================== */
279 : /* VSISubFileFilesystemHandler */
280 : /* ==================================================================== */
281 : /************************************************************************/
282 :
283 : /************************************************************************/
284 : /* DecomposePath() */
285 : /* */
286 : /* Parse a path like /vsisubfile/1000_2000,data/abc.tif into an */
287 : /* offset (1000), a size (2000) and a path (data/abc.tif). */
288 : /************************************************************************/
289 :
290 383 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
291 : CPLString &osFilename,
292 : vsi_l_offset &nSubFileOffset,
293 : vsi_l_offset &nSubFileSize)
294 :
295 : {
296 383 : if (!STARTS_WITH(pszPath, "/vsisubfile/"))
297 0 : return FALSE;
298 :
299 383 : osFilename = "";
300 383 : nSubFileOffset = 0;
301 383 : nSubFileSize = 0;
302 :
303 383 : nSubFileOffset =
304 383 : CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
305 2821 : for (int i = 12; pszPath[i] != '\0'; i++)
306 : {
307 2821 : if (pszPath[i] == '_' && nSubFileSize == 0)
308 : {
309 : // -1 is sometimes passed to mean that we don't know the file size
310 : // for example when creating a JPEG2000 datastream in a NITF file
311 : // Transform it into 0 for correct behavior of Read(), Write() and
312 : // Eof().
313 349 : if (pszPath[i + 1] == '-')
314 205 : nSubFileSize = 0;
315 : else
316 144 : nSubFileSize = CPLScanUIntBig(
317 144 : pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
318 : }
319 2472 : else if (pszPath[i] == ',')
320 : {
321 383 : osFilename = pszPath + i + 1;
322 383 : return TRUE;
323 : }
324 2089 : else if (pszPath[i] == '/')
325 : {
326 : // Missing comma!
327 0 : return FALSE;
328 : }
329 : }
330 :
331 0 : return FALSE;
332 : }
333 :
334 : /************************************************************************/
335 : /* Open() */
336 : /************************************************************************/
337 :
338 : VSIVirtualHandleUniquePtr
339 311 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
340 : const char *pszAccess, bool /* bSetError */,
341 : CSLConstList /* papszOptions */)
342 :
343 : {
344 311 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
345 1 : return nullptr;
346 :
347 620 : CPLString osSubFilePath;
348 310 : vsi_l_offset nOff = 0;
349 310 : vsi_l_offset nSize = 0;
350 :
351 310 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
352 : {
353 0 : errno = ENOENT;
354 0 : return nullptr;
355 : }
356 310 : if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
357 : {
358 0 : return nullptr;
359 : }
360 :
361 : /* -------------------------------------------------------------------- */
362 : /* We can't open the containing file with "w" access, so if */
363 : /* that is requested use "r+" instead to update in place. */
364 : /* -------------------------------------------------------------------- */
365 310 : if (pszAccess[0] == 'w')
366 5 : pszAccess = "r+";
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Open the underlying file. */
370 : /* -------------------------------------------------------------------- */
371 620 : auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess);
372 :
373 310 : if (fp == nullptr)
374 78 : return nullptr;
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Setup the file handle on this file. */
378 : /* -------------------------------------------------------------------- */
379 464 : auto poHandle = std::make_unique<VSISubFileHandle>();
380 :
381 232 : poHandle->fp = fp.release();
382 232 : poHandle->nSubregionOffset = nOff;
383 232 : poHandle->nSubregionSize = nSize;
384 :
385 : // In read-only mode validate (offset, size) against underlying file size
386 232 : if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
387 : {
388 219 : if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
389 : {
390 0 : return nullptr;
391 : }
392 219 : vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
393 : // For a directory, the size will be max(vsi_l_offset) / 2
394 219 : if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
395 : {
396 0 : return nullptr;
397 : }
398 219 : if (nOff + nSize > nFpSize)
399 : {
400 0 : nSize = nFpSize - nOff;
401 0 : poHandle->nSubregionSize = nSize;
402 : }
403 : }
404 :
405 232 : if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
406 : {
407 0 : poHandle.reset();
408 : }
409 :
410 232 : return VSIVirtualHandleUniquePtr(poHandle.release());
411 : }
412 :
413 : /************************************************************************/
414 : /* Stat() */
415 : /************************************************************************/
416 :
417 74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
418 : VSIStatBufL *psStatBuf, int nFlags)
419 :
420 : {
421 74 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
422 1 : return -1;
423 :
424 146 : CPLString osSubFilePath;
425 73 : vsi_l_offset nOff = 0;
426 73 : vsi_l_offset nSize = 0;
427 :
428 73 : memset(psStatBuf, 0, sizeof(VSIStatBufL));
429 :
430 73 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
431 : {
432 0 : errno = ENOENT;
433 0 : return -1;
434 : }
435 :
436 73 : const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
437 :
438 73 : if (nResult == 0)
439 : {
440 47 : if (nSize != 0)
441 0 : psStatBuf->st_size = nSize;
442 47 : else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
443 47 : psStatBuf->st_size -= nOff;
444 : else
445 0 : psStatBuf->st_size = 0;
446 : }
447 :
448 73 : return nResult;
449 : }
450 :
451 : /************************************************************************/
452 : /* Unlink() */
453 : /************************************************************************/
454 :
455 0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
456 : {
457 0 : errno = EACCES;
458 0 : return -1;
459 : }
460 :
461 : /************************************************************************/
462 : /* Mkdir() */
463 : /************************************************************************/
464 :
465 0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
466 : long /* nMode */)
467 : {
468 0 : errno = EACCES;
469 0 : return -1;
470 : }
471 :
472 : /************************************************************************/
473 : /* Rmdir() */
474 : /************************************************************************/
475 :
476 0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
477 :
478 : {
479 0 : errno = EACCES;
480 0 : return -1;
481 : }
482 :
483 : /************************************************************************/
484 : /* ReadDirEx() */
485 : /************************************************************************/
486 :
487 136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
488 : int /* nMaxFiles */)
489 : {
490 136 : errno = EACCES;
491 136 : return nullptr;
492 : }
493 :
494 : /************************************************************************/
495 : /* VSIInstallSubFileFilesystemHandler() */
496 : /************************************************************************/
497 :
498 : /*!
499 : \brief Install /vsisubfile/ virtual file handler.
500 :
501 : \verbatim embed:rst
502 : See :ref:`/vsisubfile/ documentation <vsisubfile>`
503 : \endverbatim
504 : */
505 :
506 1754 : void VSIInstallSubFileHandler()
507 : {
508 1754 : VSIFileManager::InstallHandler("/vsisubfile/",
509 1754 : new VSISubFileFilesystemHandler);
510 1754 : }
|