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