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