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 237 : 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 nBytes) override;
50 : size_t Write(const void *pBuffer, size_t nBytes) 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 1788 : VSISubFileFilesystemHandler() = default;
69 1126 : ~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 474 : VSISubFileHandle::~VSISubFileHandle()
93 : {
94 237 : VSISubFileHandle::Close();
95 474 : }
96 :
97 : /************************************************************************/
98 : /* Close() */
99 : /************************************************************************/
100 :
101 474 : int VSISubFileHandle::Close()
102 :
103 : {
104 474 : if (fp == nullptr)
105 237 : return -1;
106 237 : int nRet = VSIFCloseL(fp);
107 237 : fp = nullptr;
108 :
109 237 : return nRet;
110 : }
111 :
112 : /************************************************************************/
113 : /* Seek() */
114 : /************************************************************************/
115 :
116 1518 : int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
117 :
118 : {
119 1518 : bAtEOF = false;
120 :
121 1518 : if (nWhence == SEEK_SET)
122 : {
123 1399 : if (nOffset >
124 1399 : std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
125 0 : return -1;
126 1399 : nOffset += nSubregionOffset;
127 : }
128 119 : else if (nWhence == SEEK_CUR)
129 : {
130 : // handle normally.
131 : }
132 94 : else if (nWhence == SEEK_END)
133 : {
134 94 : if (nSubregionSize != 0)
135 : {
136 75 : nOffset = nSubregionOffset + nSubregionSize;
137 75 : nWhence = SEEK_SET;
138 : }
139 : }
140 : else
141 : {
142 0 : errno = EINVAL;
143 0 : return -1;
144 : }
145 :
146 1518 : return VSIFSeekL(fp, nOffset, nWhence);
147 : }
148 :
149 : /************************************************************************/
150 : /* Tell() */
151 : /************************************************************************/
152 :
153 3373 : vsi_l_offset VSISubFileHandle::Tell()
154 :
155 : {
156 3373 : vsi_l_offset nBasePos = VSIFTellL(fp);
157 3373 : if (nBasePos >= nSubregionOffset)
158 3373 : return nBasePos - nSubregionOffset;
159 0 : return 0;
160 : }
161 :
162 : /************************************************************************/
163 : /* Read() */
164 : /************************************************************************/
165 :
166 4064 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nByteToRead)
167 :
168 : {
169 4064 : if (nByteToRead == 0)
170 20 : return 0;
171 :
172 4044 : size_t nRet = 0;
173 4044 : if (nSubregionSize == 0)
174 : {
175 612 : nRet = fp->Read(pBuffer, nByteToRead);
176 : }
177 : else
178 : {
179 3432 : const vsi_l_offset nCurOffset = fp->Tell();
180 3432 : if (nCurOffset >= nSubregionOffset + nSubregionSize)
181 : {
182 5 : bAtEOF = true;
183 5 : return 0;
184 : }
185 :
186 3427 : if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize)
187 : {
188 91 : nRet = fp->Read(pBuffer,
189 91 : static_cast<size_t>(nSubregionOffset +
190 91 : nSubregionSize - nCurOffset));
191 : }
192 : else
193 : {
194 3336 : nRet = fp->Read(pBuffer, nByteToRead);
195 : }
196 : }
197 :
198 4039 : if (nRet < nByteToRead)
199 : {
200 216 : if (fp->Eof())
201 125 : bAtEOF = true;
202 : else /* if (fp->Error()) */
203 91 : bError = true;
204 : }
205 :
206 4039 : return nRet;
207 : }
208 :
209 : /************************************************************************/
210 : /* Write() */
211 : /************************************************************************/
212 :
213 2618 : size_t VSISubFileHandle::Write(const void *pBuffer, size_t nBytes)
214 :
215 : {
216 2618 : bAtEOF = false;
217 :
218 2618 : if (nBytes == 0)
219 0 : return 0;
220 :
221 2618 : if (nSubregionSize == 0)
222 2618 : return fp->Write(pBuffer, nBytes);
223 :
224 0 : const vsi_l_offset nCurOffset = VSIFTellL(fp);
225 0 : if (nCurOffset >= nSubregionOffset + nSubregionSize)
226 0 : return 0;
227 :
228 0 : if (nCurOffset + nBytes > nSubregionOffset + nSubregionSize)
229 : {
230 0 : return fp->Write(pBuffer,
231 0 : static_cast<size_t>(nSubregionOffset + nSubregionSize -
232 0 : nCurOffset));
233 : }
234 :
235 0 : return fp->Write(pBuffer, nBytes);
236 : }
237 :
238 : /************************************************************************/
239 : /* ClearErr() */
240 : /************************************************************************/
241 :
242 70 : void VSISubFileHandle::ClearErr()
243 :
244 : {
245 70 : fp->ClearErr();
246 70 : bAtEOF = false;
247 70 : bError = false;
248 70 : }
249 :
250 : /************************************************************************/
251 : /* Error() */
252 : /************************************************************************/
253 :
254 90 : int VSISubFileHandle::Error()
255 :
256 : {
257 90 : return bError;
258 : }
259 :
260 : /************************************************************************/
261 : /* Eof() */
262 : /************************************************************************/
263 :
264 94 : int VSISubFileHandle::Eof()
265 :
266 : {
267 94 : return bAtEOF;
268 : }
269 :
270 : /************************************************************************/
271 : /* ==================================================================== */
272 : /* VSISubFileFilesystemHandler */
273 : /* ==================================================================== */
274 : /************************************************************************/
275 :
276 : /************************************************************************/
277 : /* DecomposePath() */
278 : /* */
279 : /* Parse a path like /vsisubfile/1000_2000,data/abc.tif into an */
280 : /* offset (1000), a size (2000) and a path (data/abc.tif). */
281 : /************************************************************************/
282 :
283 328 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
284 : CPLString &osFilename,
285 : vsi_l_offset &nSubFileOffset,
286 : vsi_l_offset &nSubFileSize)
287 :
288 : {
289 328 : if (!STARTS_WITH(pszPath, "/vsisubfile/"))
290 0 : return FALSE;
291 :
292 328 : osFilename = "";
293 328 : nSubFileOffset = 0;
294 328 : nSubFileSize = 0;
295 :
296 328 : nSubFileOffset =
297 328 : CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
298 2424 : for (int i = 12; pszPath[i] != '\0'; i++)
299 : {
300 2424 : if (pszPath[i] == '_' && nSubFileSize == 0)
301 : {
302 : // -1 is sometimes passed to mean that we don't know the file size
303 : // for example when creating a JPEG2000 datastream in a NITF file
304 : // Transform it into 0 for correct behavior of Read(), Write() and
305 : // Eof().
306 294 : if (pszPath[i + 1] == '-')
307 145 : nSubFileSize = 0;
308 : else
309 149 : nSubFileSize = CPLScanUIntBig(
310 149 : pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
311 : }
312 2130 : else if (pszPath[i] == ',')
313 : {
314 328 : osFilename = pszPath + i + 1;
315 328 : return TRUE;
316 : }
317 1802 : else if (pszPath[i] == '/')
318 : {
319 : // Missing comma!
320 0 : return FALSE;
321 : }
322 : }
323 :
324 0 : return FALSE;
325 : }
326 :
327 : /************************************************************************/
328 : /* Open() */
329 : /************************************************************************/
330 :
331 : VSIVirtualHandleUniquePtr
332 256 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
333 : const char *pszAccess, bool bSetError,
334 : CSLConstList papszOptions)
335 :
336 : {
337 256 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
338 1 : return nullptr;
339 :
340 510 : CPLString osSubFilePath;
341 255 : vsi_l_offset nOff = 0;
342 255 : vsi_l_offset nSize = 0;
343 :
344 255 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
345 : {
346 0 : errno = ENOENT;
347 0 : return nullptr;
348 : }
349 255 : if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
350 : {
351 0 : return nullptr;
352 : }
353 :
354 : /* -------------------------------------------------------------------- */
355 : /* We can't open the containing file with "w" access, so if */
356 : /* that is requested use "r+" instead to update in place. */
357 : /* -------------------------------------------------------------------- */
358 255 : if (pszAccess[0] == 'w')
359 5 : pszAccess = "r+";
360 :
361 : /* -------------------------------------------------------------------- */
362 : /* Open the underlying file. */
363 : /* -------------------------------------------------------------------- */
364 : auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess,
365 510 : bSetError, papszOptions);
366 :
367 255 : if (fp == nullptr)
368 18 : return nullptr;
369 :
370 : /* -------------------------------------------------------------------- */
371 : /* Setup the file handle on this file. */
372 : /* -------------------------------------------------------------------- */
373 474 : auto poHandle = std::make_unique<VSISubFileHandle>();
374 :
375 237 : poHandle->fp = fp.release();
376 237 : poHandle->nSubregionOffset = nOff;
377 237 : poHandle->nSubregionSize = nSize;
378 :
379 : // In read-only mode validate (offset, size) against underlying file size
380 237 : if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
381 : {
382 224 : if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
383 : {
384 0 : return nullptr;
385 : }
386 224 : vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
387 : // For a directory, the size will be max(vsi_l_offset) / 2
388 224 : if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
389 : {
390 0 : return nullptr;
391 : }
392 224 : if (nOff + nSize > nFpSize)
393 : {
394 0 : nSize = nFpSize - nOff;
395 0 : poHandle->nSubregionSize = nSize;
396 : }
397 : }
398 :
399 237 : if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
400 : {
401 0 : poHandle.reset();
402 : }
403 :
404 237 : return VSIVirtualHandleUniquePtr(poHandle.release());
405 : }
406 :
407 : /************************************************************************/
408 : /* Stat() */
409 : /************************************************************************/
410 :
411 74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
412 : VSIStatBufL *psStatBuf, int nFlags)
413 :
414 : {
415 74 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
416 1 : return -1;
417 :
418 146 : CPLString osSubFilePath;
419 73 : vsi_l_offset nOff = 0;
420 73 : vsi_l_offset nSize = 0;
421 :
422 73 : memset(psStatBuf, 0, sizeof(VSIStatBufL));
423 :
424 73 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
425 : {
426 0 : errno = ENOENT;
427 0 : return -1;
428 : }
429 :
430 73 : const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
431 :
432 73 : if (nResult == 0)
433 : {
434 47 : if (nSize != 0)
435 0 : psStatBuf->st_size = nSize;
436 47 : else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
437 47 : psStatBuf->st_size -= nOff;
438 : else
439 0 : psStatBuf->st_size = 0;
440 : }
441 :
442 73 : return nResult;
443 : }
444 :
445 : /************************************************************************/
446 : /* Unlink() */
447 : /************************************************************************/
448 :
449 0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
450 : {
451 0 : errno = EACCES;
452 0 : return -1;
453 : }
454 :
455 : /************************************************************************/
456 : /* Mkdir() */
457 : /************************************************************************/
458 :
459 0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
460 : long /* nMode */)
461 : {
462 0 : errno = EACCES;
463 0 : return -1;
464 : }
465 :
466 : /************************************************************************/
467 : /* Rmdir() */
468 : /************************************************************************/
469 :
470 0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
471 :
472 : {
473 0 : errno = EACCES;
474 0 : return -1;
475 : }
476 :
477 : /************************************************************************/
478 : /* ReadDirEx() */
479 : /************************************************************************/
480 :
481 136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
482 : int /* nMaxFiles */)
483 : {
484 136 : errno = EACCES;
485 136 : return nullptr;
486 : }
487 :
488 : /************************************************************************/
489 : /* VSIInstallSubFileFilesystemHandler() */
490 : /************************************************************************/
491 :
492 : /*!
493 : \brief Install /vsisubfile/ virtual file handler.
494 :
495 : \verbatim embed:rst
496 : See :ref:`/vsisubfile/ documentation <vsisubfile>`
497 : \endverbatim
498 : */
499 :
500 1788 : void VSIInstallSubFileHandler()
501 : {
502 1788 : VSIFileManager::InstallHandler(
503 3576 : "/vsisubfile/", std::make_shared<VSISubFileFilesystemHandler>());
504 1788 : }
|