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 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_vsil_plugin.h"
15 :
16 : //! @cond Doxygen_Suppress
17 : #ifndef DOXYGEN_SKIP
18 :
19 : namespace cpl
20 : {
21 :
22 2 : VSIPluginHandle::VSIPluginHandle(VSIPluginFilesystemHandler *poFSIn,
23 2 : void *cbDataIn)
24 2 : : poFS(poFSIn), cbData(cbDataIn)
25 : {
26 2 : }
27 :
28 4 : VSIPluginHandle::~VSIPluginHandle()
29 : {
30 2 : if (cbData)
31 : {
32 0 : VSIPluginHandle::Close();
33 : }
34 4 : }
35 :
36 0 : int VSIPluginHandle::Seek(vsi_l_offset nOffset, int nWhence)
37 : {
38 0 : return poFS->Seek(cbData, nOffset, nWhence);
39 : }
40 :
41 0 : vsi_l_offset VSIPluginHandle::Tell()
42 : {
43 0 : return poFS->Tell(cbData);
44 : }
45 :
46 0 : size_t VSIPluginHandle::Read(void *const pBuffer, size_t const nSize,
47 : size_t const nMemb)
48 : {
49 0 : return poFS->Read(cbData, pBuffer, nSize, nMemb);
50 : }
51 :
52 0 : int VSIPluginHandle::Eof()
53 : {
54 0 : return poFS->Eof(cbData);
55 : }
56 :
57 0 : int VSIPluginHandle::Error()
58 : {
59 0 : return poFS->Error(cbData);
60 : }
61 :
62 0 : void VSIPluginHandle::ClearErr()
63 : {
64 0 : poFS->ClearErr(cbData);
65 0 : }
66 :
67 2 : int VSIPluginHandle::Close()
68 : {
69 2 : int ret = poFS->Close(cbData);
70 2 : cbData = nullptr;
71 2 : return ret;
72 : }
73 :
74 0 : int VSIPluginHandle::ReadMultiRange(int nRanges, void **ppData,
75 : const vsi_l_offset *panOffsets,
76 : const size_t *panSizes)
77 : {
78 0 : return poFS->ReadMultiRange(cbData, nRanges, ppData, panOffsets, panSizes);
79 : }
80 :
81 2 : void VSIPluginHandle::AdviseRead(int nRanges, const vsi_l_offset *panOffsets,
82 : const size_t *panSizes)
83 : {
84 2 : poFS->AdviseRead(cbData, nRanges, panOffsets, panSizes);
85 2 : }
86 :
87 0 : VSIRangeStatus VSIPluginHandle::GetRangeStatus(vsi_l_offset nOffset,
88 : vsi_l_offset nLength)
89 : {
90 0 : return poFS->GetRangeStatus(cbData, nOffset, nLength);
91 : }
92 :
93 0 : size_t VSIPluginHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
94 : {
95 0 : return poFS->Write(cbData, pBuffer, nSize, nCount);
96 : }
97 :
98 0 : int VSIPluginHandle::Flush()
99 : {
100 0 : return poFS->Flush(cbData);
101 : }
102 :
103 0 : int VSIPluginHandle::Truncate(vsi_l_offset nNewSize)
104 : {
105 0 : return poFS->Truncate(cbData, nNewSize);
106 : }
107 :
108 2 : VSIPluginFilesystemHandler::VSIPluginFilesystemHandler(
109 2 : const char *pszPrefix, const VSIFilesystemPluginCallbacksStruct *cbIn)
110 2 : : m_Prefix(pszPrefix), m_cb(nullptr)
111 : {
112 2 : m_cb = new VSIFilesystemPluginCallbacksStruct(*cbIn);
113 2 : }
114 :
115 2 : VSIPluginFilesystemHandler::~VSIPluginFilesystemHandler()
116 : {
117 1 : delete m_cb;
118 2 : }
119 :
120 : VSIVirtualHandleUniquePtr
121 3 : VSIPluginFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
122 : bool bSetError,
123 : CSLConstList /* papszOptions */)
124 : {
125 3 : if (!IsValidFilename(pszFilename))
126 0 : return nullptr;
127 3 : void *cbData = m_cb->open(m_cb->pUserData, GetCallbackFilename(pszFilename),
128 3 : pszAccess);
129 3 : if (cbData == nullptr)
130 : {
131 1 : if (bSetError)
132 : {
133 0 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(errno));
134 : }
135 1 : return nullptr;
136 : }
137 4 : auto poPluginHandle = std::make_unique<VSIPluginHandle>(this, cbData);
138 2 : if (m_cb->nBufferSize == 0)
139 : {
140 2 : return VSIVirtualHandleUniquePtr(poPluginHandle.release());
141 : }
142 : else
143 : {
144 : return VSIVirtualHandleUniquePtr(VSICreateCachedFile(
145 0 : poPluginHandle.release(), m_cb->nBufferSize,
146 0 : (m_cb->nCacheSize < m_cb->nBufferSize) ? m_cb->nBufferSize
147 0 : : m_cb->nCacheSize));
148 : }
149 : }
150 :
151 : const char *
152 3 : VSIPluginFilesystemHandler::GetCallbackFilename(const char *pszFilename)
153 : {
154 3 : return pszFilename + strlen(m_Prefix);
155 : }
156 :
157 3 : bool VSIPluginFilesystemHandler::IsValidFilename(const char *pszFilename)
158 : {
159 3 : if (!STARTS_WITH_CI(pszFilename, m_Prefix))
160 0 : return false;
161 3 : return true;
162 : }
163 :
164 0 : int VSIPluginFilesystemHandler::Stat(const char *pszFilename,
165 : VSIStatBufL *pStatBuf, int nFlags)
166 : {
167 0 : if (!IsValidFilename(pszFilename))
168 : {
169 0 : errno = EBADF;
170 0 : return -1;
171 : }
172 :
173 0 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
174 :
175 0 : int nRet = 0;
176 0 : if (m_cb->stat != nullptr)
177 : {
178 0 : nRet = m_cb->stat(m_cb->pUserData, GetCallbackFilename(pszFilename),
179 : pStatBuf, nFlags);
180 : }
181 : else
182 : {
183 0 : nRet = -1;
184 : }
185 0 : return nRet;
186 : }
187 :
188 0 : int VSIPluginFilesystemHandler::Seek(void *pFile, vsi_l_offset nOffset,
189 : int nWhence)
190 : {
191 0 : if (m_cb->seek != nullptr)
192 : {
193 0 : return m_cb->seek(pFile, nOffset, nWhence);
194 : }
195 0 : CPLError(CE_Failure, CPLE_AppDefined, "Seek not implemented for %s plugin",
196 : m_Prefix);
197 0 : return -1;
198 : }
199 :
200 0 : vsi_l_offset VSIPluginFilesystemHandler::Tell(void *pFile)
201 : {
202 0 : if (m_cb->tell != nullptr)
203 : {
204 0 : return m_cb->tell(pFile);
205 : }
206 0 : CPLError(CE_Failure, CPLE_AppDefined, "Tell not implemented for %s plugin",
207 : m_Prefix);
208 0 : return -1;
209 : }
210 :
211 0 : size_t VSIPluginFilesystemHandler::Read(void *pFile, void *pBuffer,
212 : size_t nSize, size_t nCount)
213 : {
214 0 : if (m_cb->read != nullptr)
215 : {
216 0 : return m_cb->read(pFile, pBuffer, nSize, nCount);
217 : }
218 0 : CPLError(CE_Failure, CPLE_AppDefined, "Read not implemented for %s plugin",
219 : m_Prefix);
220 0 : return -1;
221 : }
222 :
223 0 : int VSIPluginFilesystemHandler::HasOptimizedReadMultiRange(
224 : const char * /*pszPath*/)
225 : {
226 0 : if (m_cb->read_multi_range != nullptr)
227 : {
228 0 : return TRUE;
229 : }
230 0 : return FALSE;
231 : }
232 :
233 0 : VSIRangeStatus VSIPluginFilesystemHandler::GetRangeStatus(void *pFile,
234 : vsi_l_offset nOffset,
235 : vsi_l_offset nLength)
236 : {
237 0 : if (m_cb->get_range_status != nullptr)
238 : {
239 0 : return m_cb->get_range_status(pFile, nOffset, nLength);
240 : }
241 0 : return VSI_RANGE_STATUS_UNKNOWN;
242 : }
243 :
244 0 : int VSIPluginFilesystemHandler::ReadMultiRange(void *pFile, int nRanges,
245 : void **ppData,
246 : const vsi_l_offset *panOffsets,
247 : const size_t *panSizes)
248 : {
249 0 : if (m_cb->read_multi_range == nullptr)
250 : {
251 0 : CPLError(CE_Failure, CPLE_AppDefined,
252 : "Read not implemented for %s plugin", m_Prefix);
253 0 : return -1;
254 : }
255 : int iRange;
256 0 : int nMergedRanges = 1;
257 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
258 : {
259 0 : if (panOffsets[iRange] + panSizes[iRange] != panOffsets[iRange + 1])
260 : {
261 0 : nMergedRanges++;
262 : }
263 : }
264 0 : if (nMergedRanges == nRanges)
265 : {
266 0 : return m_cb->read_multi_range(pFile, nRanges, ppData, panOffsets,
267 0 : panSizes);
268 : }
269 :
270 0 : vsi_l_offset *mOffsets = new vsi_l_offset[nMergedRanges];
271 0 : size_t *mSizes = new size_t[nMergedRanges];
272 0 : char **mData = new char *[nMergedRanges];
273 :
274 0 : int curRange = 0;
275 0 : mSizes[curRange] = panSizes[0];
276 0 : mOffsets[curRange] = panOffsets[0];
277 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
278 : {
279 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
280 : {
281 0 : mSizes[curRange] += panSizes[iRange + 1];
282 : }
283 : else
284 : {
285 0 : mData[curRange] = new char[mSizes[curRange]];
286 : // start a new range
287 0 : curRange++;
288 0 : mSizes[curRange] = panSizes[iRange + 1];
289 0 : mOffsets[curRange] = panOffsets[iRange + 1];
290 : }
291 : }
292 0 : mData[curRange] = new char[mSizes[curRange]];
293 :
294 0 : int ret = m_cb->read_multi_range(pFile, nMergedRanges,
295 : reinterpret_cast<void **>(mData), mOffsets,
296 : mSizes);
297 :
298 0 : curRange = 0;
299 0 : size_t curOffset = panSizes[0];
300 0 : memcpy(ppData[0], mData[0], panSizes[0]);
301 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
302 : {
303 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
304 : {
305 0 : memcpy(ppData[iRange + 1], mData[curRange] + curOffset,
306 0 : panSizes[iRange + 1]);
307 0 : curOffset += panSizes[iRange + 1];
308 : }
309 : else
310 : {
311 0 : curRange++;
312 0 : memcpy(ppData[iRange + 1], mData[curRange], panSizes[iRange + 1]);
313 0 : curOffset = panSizes[iRange + 1];
314 : }
315 : }
316 :
317 0 : delete[] mOffsets;
318 0 : delete[] mSizes;
319 0 : for (int i = 0; i < nMergedRanges; i++)
320 : {
321 0 : delete[] mData[i];
322 : }
323 0 : delete[] mData;
324 :
325 0 : return ret;
326 : }
327 :
328 2 : void VSIPluginFilesystemHandler::AdviseRead(void *pFile, int nRanges,
329 : const vsi_l_offset *panOffsets,
330 : const size_t *panSizes)
331 : {
332 2 : if (m_cb->advise_read != nullptr)
333 : {
334 1 : m_cb->advise_read(pFile, nRanges, panOffsets, panSizes);
335 : }
336 : else
337 : {
338 1 : if (!m_bWarnedAdviseReadImplemented)
339 : {
340 1 : m_bWarnedAdviseReadImplemented = true;
341 1 : CPLDebug("VSIPlugin", "AdviseRead() not implemented");
342 : }
343 : }
344 2 : }
345 :
346 0 : int VSIPluginFilesystemHandler::Eof(void *pFile)
347 : {
348 0 : if (m_cb->eof != nullptr)
349 : {
350 0 : return m_cb->eof(pFile);
351 : }
352 0 : CPLError(CE_Failure, CPLE_AppDefined, "Eof not implemented for %s plugin",
353 : m_Prefix);
354 0 : return -1;
355 : }
356 :
357 0 : int VSIPluginFilesystemHandler::Error(void *pFile)
358 : {
359 0 : if (m_cb->error)
360 : {
361 0 : return m_cb->error(pFile);
362 : }
363 0 : CPLDebug("CPL", "Error() not implemented for %s plugin", m_Prefix);
364 0 : return 0;
365 : }
366 :
367 0 : void VSIPluginFilesystemHandler::ClearErr(void *pFile)
368 : {
369 0 : if (m_cb->clear_err)
370 : {
371 0 : m_cb->clear_err(pFile);
372 : }
373 : else
374 : {
375 0 : CPLDebug("CPL", "ClearErr() not implemented for %s plugin", m_Prefix);
376 : }
377 0 : }
378 :
379 2 : int VSIPluginFilesystemHandler::Close(void *pFile)
380 : {
381 2 : if (m_cb->close != nullptr)
382 : {
383 0 : return m_cb->close(pFile);
384 : }
385 2 : CPLError(CE_Failure, CPLE_AppDefined, "Close not implemented for %s plugin",
386 : m_Prefix);
387 2 : return -1;
388 : }
389 :
390 0 : size_t VSIPluginFilesystemHandler::Write(void *pFile, const void *psBuffer,
391 : size_t nSize, size_t nCount)
392 : {
393 0 : if (m_cb->write != nullptr)
394 : {
395 0 : return m_cb->write(pFile, psBuffer, nSize, nCount);
396 : }
397 0 : CPLError(CE_Failure, CPLE_AppDefined, "Write not implemented for %s plugin",
398 : m_Prefix);
399 0 : return -1;
400 : }
401 :
402 0 : int VSIPluginFilesystemHandler::Flush(void *pFile)
403 : {
404 0 : if (m_cb->flush != nullptr)
405 : {
406 0 : return m_cb->flush(pFile);
407 : }
408 0 : CPLError(CE_Failure, CPLE_AppDefined, "Flush not implemented for %s plugin",
409 : m_Prefix);
410 0 : return -1;
411 : }
412 :
413 0 : int VSIPluginFilesystemHandler::Truncate(void *pFile, vsi_l_offset nNewSize)
414 : {
415 0 : if (m_cb->truncate != nullptr)
416 : {
417 0 : return m_cb->truncate(pFile, nNewSize);
418 : }
419 0 : CPLError(CE_Failure, CPLE_AppDefined,
420 : "Truncate not implemented for %s plugin", m_Prefix);
421 0 : return -1;
422 : }
423 :
424 0 : char **VSIPluginFilesystemHandler::ReadDirEx(const char *pszDirname,
425 : int nMaxFiles)
426 : {
427 0 : if (!IsValidFilename(pszDirname))
428 0 : return nullptr;
429 0 : if (m_cb->read_dir != nullptr)
430 : {
431 0 : return m_cb->read_dir(m_cb->pUserData, GetCallbackFilename(pszDirname),
432 0 : nMaxFiles);
433 : }
434 0 : return nullptr;
435 : }
436 :
437 0 : char **VSIPluginFilesystemHandler::SiblingFiles(const char *pszFilename)
438 : {
439 0 : if (!IsValidFilename(pszFilename))
440 0 : return nullptr;
441 0 : if (m_cb->sibling_files != nullptr)
442 : {
443 0 : return m_cb->sibling_files(m_cb->pUserData,
444 0 : GetCallbackFilename(pszFilename));
445 : }
446 0 : return nullptr;
447 : }
448 :
449 0 : int VSIPluginFilesystemHandler::Unlink(const char *pszFilename)
450 : {
451 0 : if (m_cb->unlink == nullptr || !IsValidFilename(pszFilename))
452 0 : return -1;
453 0 : return unlink(GetCallbackFilename(pszFilename));
454 : }
455 :
456 0 : int VSIPluginFilesystemHandler::Rename(const char *oldpath, const char *newpath,
457 : GDALProgressFunc, void *)
458 : {
459 0 : if (m_cb->rename == nullptr || !IsValidFilename(oldpath) ||
460 0 : !IsValidFilename(newpath))
461 0 : return -1;
462 0 : return m_cb->rename(m_cb->pUserData, GetCallbackFilename(oldpath),
463 0 : GetCallbackFilename(newpath));
464 : }
465 :
466 0 : int VSIPluginFilesystemHandler::Mkdir(const char *pszDirname, long nMode)
467 : {
468 0 : if (m_cb->mkdir == nullptr || !IsValidFilename(pszDirname))
469 0 : return -1;
470 0 : return m_cb->mkdir(m_cb->pUserData, GetCallbackFilename(pszDirname), nMode);
471 : }
472 :
473 0 : int VSIPluginFilesystemHandler::Rmdir(const char *pszDirname)
474 : {
475 0 : if (m_cb->rmdir == nullptr || !IsValidFilename(pszDirname))
476 0 : return -1;
477 0 : return m_cb->rmdir(m_cb->pUserData, GetCallbackFilename(pszDirname));
478 : }
479 : } // namespace cpl
480 :
481 : #endif // DOXYGEN_SKIP
482 : //! @endcond
483 :
484 2 : int VSIInstallPluginHandler(const char *pszPrefix,
485 : const VSIFilesystemPluginCallbacksStruct *poCb)
486 : {
487 : VSIFilesystemHandler *poHandler =
488 2 : new cpl::VSIPluginFilesystemHandler(pszPrefix, poCb);
489 : // TODO: check pszPrefix starts and ends with a /
490 2 : VSIFileManager::InstallHandler(pszPrefix, poHandler);
491 2 : return 0;
492 : }
493 :
494 3 : int VSIRemovePluginHandler(const char *pszPrefix)
495 : {
496 3 : VSIFileManager::RemoveHandler(pszPrefix);
497 3 : return 0;
498 : }
499 :
500 : VSIFilesystemPluginCallbacksStruct *
501 2 : VSIAllocFilesystemPluginCallbacksStruct(void)
502 : {
503 : return static_cast<VSIFilesystemPluginCallbacksStruct *>(
504 2 : VSI_CALLOC_VERBOSE(1, sizeof(VSIFilesystemPluginCallbacksStruct)));
505 : }
506 :
507 2 : void VSIFreeFilesystemPluginCallbacksStruct(
508 : VSIFilesystemPluginCallbacksStruct *poCb)
509 : {
510 2 : CPLFree(poCb);
511 2 : }
|