Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for plugins
5 : * Author: Thomas Bonfort, thomas.bonfort@airbus.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Thomas Bonfort <thomas.bonfort@airbus.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_port.h"
30 : #include "cpl_vsil_plugin.h"
31 :
32 : //! @cond Doxygen_Suppress
33 : #ifndef DOXYGEN_SKIP
34 :
35 : namespace cpl
36 : {
37 :
38 2 : VSIPluginHandle::VSIPluginHandle(VSIPluginFilesystemHandler *poFSIn,
39 2 : void *cbDataIn)
40 2 : : poFS(poFSIn), cbData(cbDataIn)
41 : {
42 2 : }
43 :
44 4 : VSIPluginHandle::~VSIPluginHandle()
45 : {
46 2 : if (cbData)
47 : {
48 0 : VSIPluginHandle::Close();
49 : }
50 4 : }
51 :
52 0 : int VSIPluginHandle::Seek(vsi_l_offset nOffset, int nWhence)
53 : {
54 0 : return poFS->Seek(cbData, nOffset, nWhence);
55 : }
56 :
57 0 : vsi_l_offset VSIPluginHandle::Tell()
58 : {
59 0 : return poFS->Tell(cbData);
60 : }
61 :
62 0 : size_t VSIPluginHandle::Read(void *const pBuffer, size_t const nSize,
63 : size_t const nMemb)
64 : {
65 0 : return poFS->Read(cbData, pBuffer, nSize, nMemb);
66 : }
67 :
68 0 : int VSIPluginHandle::Eof()
69 : {
70 0 : return poFS->Eof(cbData);
71 : }
72 :
73 2 : int VSIPluginHandle::Close()
74 : {
75 2 : int ret = poFS->Close(cbData);
76 2 : cbData = nullptr;
77 2 : return ret;
78 : }
79 :
80 0 : int VSIPluginHandle::ReadMultiRange(int nRanges, void **ppData,
81 : const vsi_l_offset *panOffsets,
82 : const size_t *panSizes)
83 : {
84 0 : return poFS->ReadMultiRange(cbData, nRanges, ppData, panOffsets, panSizes);
85 : }
86 :
87 2 : void VSIPluginHandle::AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
88 : const size_t *panSizes)
89 : {
90 2 : poFS->AdviseRead(cbData, nRanges, panOffsets, panSizes);
91 2 : }
92 :
93 0 : VSIRangeStatus VSIPluginHandle::GetRangeStatus(vsi_l_offset nOffset,
94 : vsi_l_offset nLength)
95 : {
96 0 : return poFS->GetRangeStatus(cbData, nOffset, nLength);
97 : }
98 :
99 0 : size_t VSIPluginHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
100 : {
101 0 : return poFS->Write(cbData, pBuffer, nSize, nCount);
102 : }
103 :
104 0 : int VSIPluginHandle::Flush()
105 : {
106 0 : return poFS->Flush(cbData);
107 : }
108 :
109 0 : int VSIPluginHandle::Truncate(vsi_l_offset nNewSize)
110 : {
111 0 : return poFS->Truncate(cbData, nNewSize);
112 : }
113 :
114 2 : VSIPluginFilesystemHandler::VSIPluginFilesystemHandler(
115 2 : const char *pszPrefix, const VSIFilesystemPluginCallbacksStruct *cbIn)
116 2 : : m_Prefix(pszPrefix), m_cb(nullptr)
117 : {
118 2 : m_cb = new VSIFilesystemPluginCallbacksStruct(*cbIn);
119 2 : }
120 :
121 2 : VSIPluginFilesystemHandler::~VSIPluginFilesystemHandler()
122 : {
123 1 : delete m_cb;
124 2 : }
125 :
126 : VSIVirtualHandle *
127 3 : VSIPluginFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
128 : bool bSetError,
129 : CSLConstList /* papszOptions */)
130 : {
131 3 : if (!IsValidFilename(pszFilename))
132 0 : return nullptr;
133 3 : void *cbData = m_cb->open(m_cb->pUserData, GetCallbackFilename(pszFilename),
134 : pszAccess);
135 3 : if (cbData == nullptr)
136 : {
137 1 : if (bSetError)
138 : {
139 0 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(errno));
140 : }
141 1 : return nullptr;
142 : }
143 2 : if (m_cb->nBufferSize == 0)
144 : {
145 2 : return new VSIPluginHandle(this, cbData);
146 : }
147 : else
148 : {
149 0 : return VSICreateCachedFile(
150 0 : new VSIPluginHandle(this, cbData), m_cb->nBufferSize,
151 0 : (m_cb->nCacheSize < m_cb->nBufferSize) ? m_cb->nBufferSize
152 0 : : m_cb->nCacheSize);
153 : }
154 : }
155 :
156 : const char *
157 3 : VSIPluginFilesystemHandler::GetCallbackFilename(const char *pszFilename)
158 : {
159 3 : return pszFilename + strlen(m_Prefix);
160 : }
161 :
162 3 : bool VSIPluginFilesystemHandler::IsValidFilename(const char *pszFilename)
163 : {
164 3 : if (!STARTS_WITH_CI(pszFilename, m_Prefix))
165 0 : return false;
166 3 : return true;
167 : }
168 :
169 0 : int VSIPluginFilesystemHandler::Stat(const char *pszFilename,
170 : VSIStatBufL *pStatBuf, int nFlags)
171 : {
172 0 : if (!IsValidFilename(pszFilename))
173 : {
174 0 : errno = EBADF;
175 0 : return -1;
176 : }
177 :
178 0 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
179 :
180 0 : int nRet = 0;
181 0 : if (m_cb->stat != nullptr)
182 : {
183 0 : nRet = m_cb->stat(m_cb->pUserData, GetCallbackFilename(pszFilename),
184 : pStatBuf, nFlags);
185 : }
186 : else
187 : {
188 0 : nRet = -1;
189 : }
190 0 : return nRet;
191 : }
192 :
193 0 : int VSIPluginFilesystemHandler::Seek(void *pFile, vsi_l_offset nOffset,
194 : int nWhence)
195 : {
196 0 : if (m_cb->seek != nullptr)
197 : {
198 0 : return m_cb->seek(pFile, nOffset, nWhence);
199 : }
200 0 : CPLError(CE_Failure, CPLE_AppDefined, "Seek not implemented for %s plugin",
201 : m_Prefix);
202 0 : return -1;
203 : }
204 :
205 0 : vsi_l_offset VSIPluginFilesystemHandler::Tell(void *pFile)
206 : {
207 0 : if (m_cb->tell != nullptr)
208 : {
209 0 : return m_cb->tell(pFile);
210 : }
211 0 : CPLError(CE_Failure, CPLE_AppDefined, "Tell not implemented for %s plugin",
212 : m_Prefix);
213 0 : return -1;
214 : }
215 :
216 0 : size_t VSIPluginFilesystemHandler::Read(void *pFile, void *pBuffer,
217 : size_t nSize, size_t nCount)
218 : {
219 0 : if (m_cb->read != nullptr)
220 : {
221 0 : return m_cb->read(pFile, pBuffer, nSize, nCount);
222 : }
223 0 : CPLError(CE_Failure, CPLE_AppDefined, "Read not implemented for %s plugin",
224 : m_Prefix);
225 0 : return -1;
226 : }
227 :
228 0 : int VSIPluginFilesystemHandler::HasOptimizedReadMultiRange(
229 : const char * /*pszPath*/)
230 : {
231 0 : if (m_cb->read_multi_range != nullptr)
232 : {
233 0 : return TRUE;
234 : }
235 0 : return FALSE;
236 : }
237 :
238 0 : VSIRangeStatus VSIPluginFilesystemHandler::GetRangeStatus(void *pFile,
239 : vsi_l_offset nOffset,
240 : vsi_l_offset nLength)
241 : {
242 0 : if (m_cb->get_range_status != nullptr)
243 : {
244 0 : return m_cb->get_range_status(pFile, nOffset, nLength);
245 : }
246 0 : return VSI_RANGE_STATUS_UNKNOWN;
247 : }
248 :
249 0 : int VSIPluginFilesystemHandler::ReadMultiRange(void *pFile, int nRanges,
250 : void **ppData,
251 : const vsi_l_offset *panOffsets,
252 : const size_t *panSizes)
253 : {
254 0 : if (m_cb->read_multi_range == nullptr)
255 : {
256 0 : CPLError(CE_Failure, CPLE_AppDefined,
257 : "Read not implemented for %s plugin", m_Prefix);
258 0 : return -1;
259 : }
260 : int iRange;
261 0 : int nMergedRanges = 1;
262 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
263 : {
264 0 : if (panOffsets[iRange] + panSizes[iRange] != panOffsets[iRange + 1])
265 : {
266 0 : nMergedRanges++;
267 : }
268 : }
269 0 : if (nMergedRanges == nRanges)
270 : {
271 0 : return m_cb->read_multi_range(pFile, nRanges, ppData, panOffsets,
272 0 : panSizes);
273 : }
274 :
275 0 : vsi_l_offset *mOffsets = new vsi_l_offset[nMergedRanges];
276 0 : size_t *mSizes = new size_t[nMergedRanges];
277 0 : char **mData = new char *[nMergedRanges];
278 :
279 0 : int curRange = 0;
280 0 : mSizes[curRange] = panSizes[0];
281 0 : mOffsets[curRange] = panOffsets[0];
282 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
283 : {
284 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
285 : {
286 0 : mSizes[curRange] += panSizes[iRange + 1];
287 : }
288 : else
289 : {
290 0 : mData[curRange] = new char[mSizes[curRange]];
291 : // start a new range
292 0 : curRange++;
293 0 : mSizes[curRange] = panSizes[iRange + 1];
294 0 : mOffsets[curRange] = panOffsets[iRange + 1];
295 : }
296 : }
297 0 : mData[curRange] = new char[mSizes[curRange]];
298 :
299 0 : int ret = m_cb->read_multi_range(pFile, nMergedRanges,
300 : reinterpret_cast<void **>(mData), mOffsets,
301 : mSizes);
302 :
303 0 : curRange = 0;
304 0 : size_t curOffset = panSizes[0];
305 0 : memcpy(ppData[0], mData[0], panSizes[0]);
306 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
307 : {
308 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
309 : {
310 0 : memcpy(ppData[iRange + 1], mData[curRange] + curOffset,
311 0 : panSizes[iRange + 1]);
312 0 : curOffset += panSizes[iRange + 1];
313 : }
314 : else
315 : {
316 0 : curRange++;
317 0 : memcpy(ppData[iRange + 1], mData[curRange], panSizes[iRange + 1]);
318 0 : curOffset = panSizes[iRange + 1];
319 : }
320 : }
321 :
322 0 : delete[] mOffsets;
323 0 : delete[] mSizes;
324 0 : for (int i = 0; i < nMergedRanges; i++)
325 : {
326 0 : delete[] mData[i];
327 : }
328 0 : delete[] mData;
329 :
330 0 : return ret;
331 : }
332 :
333 2 : void VSIPluginFilesystemHandler::AdviseRead(void *pFile, int nRanges,
334 : const vsi_l_offset *panOffsets,
335 : const size_t *panSizes)
336 : {
337 2 : if (m_cb->advise_read != nullptr)
338 : {
339 1 : m_cb->advise_read(pFile, nRanges, panOffsets, panSizes);
340 : }
341 : else
342 : {
343 1 : if (!m_bWarnedAdviseReadImplemented)
344 : {
345 1 : m_bWarnedAdviseReadImplemented = true;
346 1 : CPLDebug("VSIPlugin", "AdviseRead() not implemented");
347 : }
348 : }
349 2 : }
350 :
351 0 : int VSIPluginFilesystemHandler::Eof(void *pFile)
352 : {
353 0 : if (m_cb->eof != nullptr)
354 : {
355 0 : return m_cb->eof(pFile);
356 : }
357 0 : CPLError(CE_Failure, CPLE_AppDefined, "Eof not implemented for %s plugin",
358 : m_Prefix);
359 0 : return -1;
360 : }
361 :
362 2 : int VSIPluginFilesystemHandler::Close(void *pFile)
363 : {
364 2 : if (m_cb->close != nullptr)
365 : {
366 0 : return m_cb->close(pFile);
367 : }
368 2 : CPLError(CE_Failure, CPLE_AppDefined, "Close not implemented for %s plugin",
369 : m_Prefix);
370 2 : return -1;
371 : }
372 :
373 0 : size_t VSIPluginFilesystemHandler::Write(void *pFile, const void *psBuffer,
374 : size_t nSize, size_t nCount)
375 : {
376 0 : if (m_cb->write != nullptr)
377 : {
378 0 : return m_cb->write(pFile, psBuffer, nSize, nCount);
379 : }
380 0 : CPLError(CE_Failure, CPLE_AppDefined, "Write not implemented for %s plugin",
381 : m_Prefix);
382 0 : return -1;
383 : }
384 :
385 0 : int VSIPluginFilesystemHandler::Flush(void *pFile)
386 : {
387 0 : if (m_cb->flush != nullptr)
388 : {
389 0 : return m_cb->flush(pFile);
390 : }
391 0 : CPLError(CE_Failure, CPLE_AppDefined, "Flush not implemented for %s plugin",
392 : m_Prefix);
393 0 : return -1;
394 : }
395 :
396 0 : int VSIPluginFilesystemHandler::Truncate(void *pFile, vsi_l_offset nNewSize)
397 : {
398 0 : if (m_cb->truncate != nullptr)
399 : {
400 0 : return m_cb->truncate(pFile, nNewSize);
401 : }
402 0 : CPLError(CE_Failure, CPLE_AppDefined,
403 : "Truncate not implemented for %s plugin", m_Prefix);
404 0 : return -1;
405 : }
406 :
407 0 : char **VSIPluginFilesystemHandler::ReadDirEx(const char *pszDirname,
408 : int nMaxFiles)
409 : {
410 0 : if (!IsValidFilename(pszDirname))
411 0 : return nullptr;
412 0 : if (m_cb->read_dir != nullptr)
413 : {
414 0 : return m_cb->read_dir(m_cb->pUserData, GetCallbackFilename(pszDirname),
415 0 : nMaxFiles);
416 : }
417 0 : return nullptr;
418 : }
419 :
420 0 : char **VSIPluginFilesystemHandler::SiblingFiles(const char *pszFilename)
421 : {
422 0 : if (!IsValidFilename(pszFilename))
423 0 : return nullptr;
424 0 : if (m_cb->sibling_files != nullptr)
425 : {
426 0 : return m_cb->sibling_files(m_cb->pUserData,
427 0 : GetCallbackFilename(pszFilename));
428 : }
429 0 : return nullptr;
430 : }
431 :
432 0 : int VSIPluginFilesystemHandler::Unlink(const char *pszFilename)
433 : {
434 0 : if (m_cb->unlink == nullptr || !IsValidFilename(pszFilename))
435 0 : return -1;
436 0 : return unlink(GetCallbackFilename(pszFilename));
437 : }
438 :
439 0 : int VSIPluginFilesystemHandler::Rename(const char *oldpath, const char *newpath)
440 : {
441 0 : if (m_cb->rename == nullptr || !IsValidFilename(oldpath) ||
442 0 : !IsValidFilename(newpath))
443 0 : return -1;
444 0 : return m_cb->rename(m_cb->pUserData, GetCallbackFilename(oldpath),
445 0 : GetCallbackFilename(newpath));
446 : }
447 :
448 0 : int VSIPluginFilesystemHandler::Mkdir(const char *pszDirname, long nMode)
449 : {
450 0 : if (m_cb->mkdir == nullptr || !IsValidFilename(pszDirname))
451 0 : return -1;
452 0 : return m_cb->mkdir(m_cb->pUserData, GetCallbackFilename(pszDirname), nMode);
453 : }
454 :
455 0 : int VSIPluginFilesystemHandler::Rmdir(const char *pszDirname)
456 : {
457 0 : if (m_cb->rmdir == nullptr || !IsValidFilename(pszDirname))
458 0 : return -1;
459 0 : return m_cb->rmdir(m_cb->pUserData, GetCallbackFilename(pszDirname));
460 : }
461 : } // namespace cpl
462 :
463 : #endif // DOXYGEN_SKIP
464 : //! @endcond
465 :
466 2 : int VSIInstallPluginHandler(const char *pszPrefix,
467 : const VSIFilesystemPluginCallbacksStruct *poCb)
468 : {
469 : VSIFilesystemHandler *poHandler =
470 2 : new cpl::VSIPluginFilesystemHandler(pszPrefix, poCb);
471 : // TODO: check pszPrefix starts and ends with a /
472 2 : VSIFileManager::InstallHandler(pszPrefix, poHandler);
473 2 : return 0;
474 : }
475 :
476 3 : int VSIRemovePluginHandler(const char *pszPrefix)
477 : {
478 3 : VSIFileManager::RemoveHandler(pszPrefix);
479 3 : return 0;
480 : }
481 :
482 : VSIFilesystemPluginCallbacksStruct *
483 2 : VSIAllocFilesystemPluginCallbacksStruct(void)
484 : {
485 : return static_cast<VSIFilesystemPluginCallbacksStruct *>(
486 2 : VSI_CALLOC_VERBOSE(1, sizeof(VSIFilesystemPluginCallbacksStruct)));
487 : }
488 :
489 2 : void VSIFreeFilesystemPluginCallbacksStruct(
490 : VSIFilesystemPluginCallbacksStruct *poCb)
491 : {
492 2 : CPLFree(poCb);
493 2 : }
|