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 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 1763 : VSISubFileFilesystemHandler() = default;
69 2244 : ~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 3305 : vsi_l_offset VSISubFileHandle::Tell()
154 :
155 : {
156 3305 : vsi_l_offset nBasePos = VSIFTellL(fp);
157 3305 : if (nBasePos >= nSubregionOffset)
158 3305 : return nBasePos - nSubregionOffset;
159 0 : return 0;
160 : }
161 :
162 : /************************************************************************/
163 : /* Read() */
164 : /************************************************************************/
165 :
166 4064 : size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
167 :
168 : {
169 4064 : size_t nRet = 0;
170 4064 : if (nSubregionSize == 0)
171 : {
172 632 : nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
173 : }
174 : else
175 : {
176 3432 : if (nSize == 0)
177 0 : return 0;
178 :
179 3432 : const vsi_l_offset nCurOffset = VSIFTellL(fp);
180 3432 : if (nCurOffset >= nSubregionOffset + nSubregionSize)
181 : {
182 5 : bAtEOF = true;
183 5 : return 0;
184 : }
185 :
186 3427 : const size_t nByteToRead = nSize * nCount;
187 3427 : 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 3336 : nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
199 : }
200 : }
201 :
202 4059 : 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 4059 : 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 328 : int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
291 : CPLString &osFilename,
292 : vsi_l_offset &nSubFileOffset,
293 : vsi_l_offset &nSubFileSize)
294 :
295 : {
296 328 : if (!STARTS_WITH(pszPath, "/vsisubfile/"))
297 0 : return FALSE;
298 :
299 328 : osFilename = "";
300 328 : nSubFileOffset = 0;
301 328 : nSubFileSize = 0;
302 :
303 328 : nSubFileOffset =
304 328 : CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
305 2424 : for (int i = 12; pszPath[i] != '\0'; i++)
306 : {
307 2424 : 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 294 : if (pszPath[i + 1] == '-')
314 145 : nSubFileSize = 0;
315 : else
316 149 : nSubFileSize = CPLScanUIntBig(
317 149 : pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
318 : }
319 2130 : else if (pszPath[i] == ',')
320 : {
321 328 : osFilename = pszPath + i + 1;
322 328 : return TRUE;
323 : }
324 1802 : 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 256 : VSISubFileFilesystemHandler::Open(const char *pszFilename,
340 : const char *pszAccess, bool bSetError,
341 : CSLConstList papszOptions)
342 :
343 : {
344 256 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
345 1 : return nullptr;
346 :
347 510 : CPLString osSubFilePath;
348 255 : vsi_l_offset nOff = 0;
349 255 : vsi_l_offset nSize = 0;
350 :
351 255 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
352 : {
353 0 : errno = ENOENT;
354 0 : return nullptr;
355 : }
356 255 : 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 255 : if (pszAccess[0] == 'w')
366 5 : pszAccess = "r+";
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Open the underlying file. */
370 : /* -------------------------------------------------------------------- */
371 : auto fp = VSIFilesystemHandler::OpenStatic(osSubFilePath, pszAccess,
372 510 : bSetError, papszOptions);
373 :
374 255 : if (fp == nullptr)
375 18 : return nullptr;
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* Setup the file handle on this file. */
379 : /* -------------------------------------------------------------------- */
380 474 : auto poHandle = std::make_unique<VSISubFileHandle>();
381 :
382 237 : poHandle->fp = fp.release();
383 237 : poHandle->nSubregionOffset = nOff;
384 237 : poHandle->nSubregionSize = nSize;
385 :
386 : // In read-only mode validate (offset, size) against underlying file size
387 237 : if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
388 : {
389 224 : if (VSIFSeekL(poHandle->fp, 0, SEEK_END) != 0)
390 : {
391 0 : return nullptr;
392 : }
393 224 : vsi_l_offset nFpSize = VSIFTellL(poHandle->fp);
394 : // For a directory, the size will be max(vsi_l_offset) / 2
395 224 : if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
396 : {
397 0 : return nullptr;
398 : }
399 224 : if (nOff + nSize > nFpSize)
400 : {
401 0 : nSize = nFpSize - nOff;
402 0 : poHandle->nSubregionSize = nSize;
403 : }
404 : }
405 :
406 237 : if (VSIFSeekL(poHandle->fp, nOff, SEEK_SET) != 0)
407 : {
408 0 : poHandle.reset();
409 : }
410 :
411 237 : return VSIVirtualHandleUniquePtr(poHandle.release());
412 : }
413 :
414 : /************************************************************************/
415 : /* Stat() */
416 : /************************************************************************/
417 :
418 74 : int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
419 : VSIStatBufL *psStatBuf, int nFlags)
420 :
421 : {
422 74 : if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
423 1 : return -1;
424 :
425 146 : CPLString osSubFilePath;
426 73 : vsi_l_offset nOff = 0;
427 73 : vsi_l_offset nSize = 0;
428 :
429 73 : memset(psStatBuf, 0, sizeof(VSIStatBufL));
430 :
431 73 : if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
432 : {
433 0 : errno = ENOENT;
434 0 : return -1;
435 : }
436 :
437 73 : const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
438 :
439 73 : if (nResult == 0)
440 : {
441 47 : if (nSize != 0)
442 0 : psStatBuf->st_size = nSize;
443 47 : else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
444 47 : psStatBuf->st_size -= nOff;
445 : else
446 0 : psStatBuf->st_size = 0;
447 : }
448 :
449 73 : return nResult;
450 : }
451 :
452 : /************************************************************************/
453 : /* Unlink() */
454 : /************************************************************************/
455 :
456 0 : int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
457 : {
458 0 : errno = EACCES;
459 0 : return -1;
460 : }
461 :
462 : /************************************************************************/
463 : /* Mkdir() */
464 : /************************************************************************/
465 :
466 0 : int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
467 : long /* nMode */)
468 : {
469 0 : errno = EACCES;
470 0 : return -1;
471 : }
472 :
473 : /************************************************************************/
474 : /* Rmdir() */
475 : /************************************************************************/
476 :
477 0 : int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
478 :
479 : {
480 0 : errno = EACCES;
481 0 : return -1;
482 : }
483 :
484 : /************************************************************************/
485 : /* ReadDirEx() */
486 : /************************************************************************/
487 :
488 136 : char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
489 : int /* nMaxFiles */)
490 : {
491 136 : errno = EACCES;
492 136 : return nullptr;
493 : }
494 :
495 : /************************************************************************/
496 : /* VSIInstallSubFileFilesystemHandler() */
497 : /************************************************************************/
498 :
499 : /*!
500 : \brief Install /vsisubfile/ virtual file handler.
501 :
502 : \verbatim embed:rst
503 : See :ref:`/vsisubfile/ documentation <vsisubfile>`
504 : \endverbatim
505 : */
506 :
507 1763 : void VSIInstallSubFileHandler()
508 : {
509 1763 : VSIFileManager::InstallHandler("/vsisubfile/",
510 1763 : new VSISubFileFilesystemHandler);
511 1763 : }
|