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 : VSIVirtualHandle *
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 : 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 2 : if (m_cb->nBufferSize == 0)
138 : {
139 2 : return new VSIPluginHandle(this, cbData);
140 : }
141 : else
142 : {
143 0 : return VSICreateCachedFile(
144 0 : new VSIPluginHandle(this, cbData), m_cb->nBufferSize,
145 0 : (m_cb->nCacheSize < m_cb->nBufferSize) ? m_cb->nBufferSize
146 0 : : m_cb->nCacheSize);
147 : }
148 : }
149 :
150 : const char *
151 3 : VSIPluginFilesystemHandler::GetCallbackFilename(const char *pszFilename)
152 : {
153 3 : return pszFilename + strlen(m_Prefix);
154 : }
155 :
156 3 : bool VSIPluginFilesystemHandler::IsValidFilename(const char *pszFilename)
157 : {
158 3 : if (!STARTS_WITH_CI(pszFilename, m_Prefix))
159 0 : return false;
160 3 : return true;
161 : }
162 :
163 0 : int VSIPluginFilesystemHandler::Stat(const char *pszFilename,
164 : VSIStatBufL *pStatBuf, int nFlags)
165 : {
166 0 : if (!IsValidFilename(pszFilename))
167 : {
168 0 : errno = EBADF;
169 0 : return -1;
170 : }
171 :
172 0 : memset(pStatBuf, 0, sizeof(VSIStatBufL));
173 :
174 0 : int nRet = 0;
175 0 : if (m_cb->stat != nullptr)
176 : {
177 0 : nRet = m_cb->stat(m_cb->pUserData, GetCallbackFilename(pszFilename),
178 : pStatBuf, nFlags);
179 : }
180 : else
181 : {
182 0 : nRet = -1;
183 : }
184 0 : return nRet;
185 : }
186 :
187 0 : int VSIPluginFilesystemHandler::Seek(void *pFile, vsi_l_offset nOffset,
188 : int nWhence)
189 : {
190 0 : if (m_cb->seek != nullptr)
191 : {
192 0 : return m_cb->seek(pFile, nOffset, nWhence);
193 : }
194 0 : CPLError(CE_Failure, CPLE_AppDefined, "Seek not implemented for %s plugin",
195 : m_Prefix);
196 0 : return -1;
197 : }
198 :
199 0 : vsi_l_offset VSIPluginFilesystemHandler::Tell(void *pFile)
200 : {
201 0 : if (m_cb->tell != nullptr)
202 : {
203 0 : return m_cb->tell(pFile);
204 : }
205 0 : CPLError(CE_Failure, CPLE_AppDefined, "Tell not implemented for %s plugin",
206 : m_Prefix);
207 0 : return -1;
208 : }
209 :
210 0 : size_t VSIPluginFilesystemHandler::Read(void *pFile, void *pBuffer,
211 : size_t nSize, size_t nCount)
212 : {
213 0 : if (m_cb->read != nullptr)
214 : {
215 0 : return m_cb->read(pFile, pBuffer, nSize, nCount);
216 : }
217 0 : CPLError(CE_Failure, CPLE_AppDefined, "Read not implemented for %s plugin",
218 : m_Prefix);
219 0 : return -1;
220 : }
221 :
222 0 : int VSIPluginFilesystemHandler::HasOptimizedReadMultiRange(
223 : const char * /*pszPath*/)
224 : {
225 0 : if (m_cb->read_multi_range != nullptr)
226 : {
227 0 : return TRUE;
228 : }
229 0 : return FALSE;
230 : }
231 :
232 0 : VSIRangeStatus VSIPluginFilesystemHandler::GetRangeStatus(void *pFile,
233 : vsi_l_offset nOffset,
234 : vsi_l_offset nLength)
235 : {
236 0 : if (m_cb->get_range_status != nullptr)
237 : {
238 0 : return m_cb->get_range_status(pFile, nOffset, nLength);
239 : }
240 0 : return VSI_RANGE_STATUS_UNKNOWN;
241 : }
242 :
243 0 : int VSIPluginFilesystemHandler::ReadMultiRange(void *pFile, int nRanges,
244 : void **ppData,
245 : const vsi_l_offset *panOffsets,
246 : const size_t *panSizes)
247 : {
248 0 : if (m_cb->read_multi_range == nullptr)
249 : {
250 0 : CPLError(CE_Failure, CPLE_AppDefined,
251 : "Read not implemented for %s plugin", m_Prefix);
252 0 : return -1;
253 : }
254 : int iRange;
255 0 : int nMergedRanges = 1;
256 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
257 : {
258 0 : if (panOffsets[iRange] + panSizes[iRange] != panOffsets[iRange + 1])
259 : {
260 0 : nMergedRanges++;
261 : }
262 : }
263 0 : if (nMergedRanges == nRanges)
264 : {
265 0 : return m_cb->read_multi_range(pFile, nRanges, ppData, panOffsets,
266 0 : panSizes);
267 : }
268 :
269 0 : vsi_l_offset *mOffsets = new vsi_l_offset[nMergedRanges];
270 0 : size_t *mSizes = new size_t[nMergedRanges];
271 0 : char **mData = new char *[nMergedRanges];
272 :
273 0 : int curRange = 0;
274 0 : mSizes[curRange] = panSizes[0];
275 0 : mOffsets[curRange] = panOffsets[0];
276 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
277 : {
278 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
279 : {
280 0 : mSizes[curRange] += panSizes[iRange + 1];
281 : }
282 : else
283 : {
284 0 : mData[curRange] = new char[mSizes[curRange]];
285 : // start a new range
286 0 : curRange++;
287 0 : mSizes[curRange] = panSizes[iRange + 1];
288 0 : mOffsets[curRange] = panOffsets[iRange + 1];
289 : }
290 : }
291 0 : mData[curRange] = new char[mSizes[curRange]];
292 :
293 0 : int ret = m_cb->read_multi_range(pFile, nMergedRanges,
294 : reinterpret_cast<void **>(mData), mOffsets,
295 : mSizes);
296 :
297 0 : curRange = 0;
298 0 : size_t curOffset = panSizes[0];
299 0 : memcpy(ppData[0], mData[0], panSizes[0]);
300 0 : for (iRange = 0; iRange < nRanges - 1; iRange++)
301 : {
302 0 : if (panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1])
303 : {
304 0 : memcpy(ppData[iRange + 1], mData[curRange] + curOffset,
305 0 : panSizes[iRange + 1]);
306 0 : curOffset += panSizes[iRange + 1];
307 : }
308 : else
309 : {
310 0 : curRange++;
311 0 : memcpy(ppData[iRange + 1], mData[curRange], panSizes[iRange + 1]);
312 0 : curOffset = panSizes[iRange + 1];
313 : }
314 : }
315 :
316 0 : delete[] mOffsets;
317 0 : delete[] mSizes;
318 0 : for (int i = 0; i < nMergedRanges; i++)
319 : {
320 0 : delete[] mData[i];
321 : }
322 0 : delete[] mData;
323 :
324 0 : return ret;
325 : }
326 :
327 2 : void VSIPluginFilesystemHandler::AdviseRead(void *pFile, int nRanges,
328 : const vsi_l_offset *panOffsets,
329 : const size_t *panSizes)
330 : {
331 2 : if (m_cb->advise_read != nullptr)
332 : {
333 1 : m_cb->advise_read(pFile, nRanges, panOffsets, panSizes);
334 : }
335 : else
336 : {
337 1 : if (!m_bWarnedAdviseReadImplemented)
338 : {
339 1 : m_bWarnedAdviseReadImplemented = true;
340 1 : CPLDebug("VSIPlugin", "AdviseRead() not implemented");
341 : }
342 : }
343 2 : }
344 :
345 0 : int VSIPluginFilesystemHandler::Eof(void *pFile)
346 : {
347 0 : if (m_cb->eof != nullptr)
348 : {
349 0 : return m_cb->eof(pFile);
350 : }
351 0 : CPLError(CE_Failure, CPLE_AppDefined, "Eof not implemented for %s plugin",
352 : m_Prefix);
353 0 : return -1;
354 : }
355 :
356 0 : int VSIPluginFilesystemHandler::Error(void *pFile)
357 : {
358 0 : if (m_cb->error)
359 : {
360 0 : return m_cb->error(pFile);
361 : }
362 0 : CPLDebug("CPL", "Error() not implemented for %s plugin", m_Prefix);
363 0 : return 0;
364 : }
365 :
366 0 : void VSIPluginFilesystemHandler::ClearErr(void *pFile)
367 : {
368 0 : if (m_cb->clear_err)
369 : {
370 0 : m_cb->clear_err(pFile);
371 : }
372 : else
373 : {
374 0 : CPLDebug("CPL", "ClearErr() not implemented for %s plugin", m_Prefix);
375 : }
376 0 : }
377 :
378 2 : int VSIPluginFilesystemHandler::Close(void *pFile)
379 : {
380 2 : if (m_cb->close != nullptr)
381 : {
382 0 : return m_cb->close(pFile);
383 : }
384 2 : CPLError(CE_Failure, CPLE_AppDefined, "Close not implemented for %s plugin",
385 : m_Prefix);
386 2 : return -1;
387 : }
388 :
389 0 : size_t VSIPluginFilesystemHandler::Write(void *pFile, const void *psBuffer,
390 : size_t nSize, size_t nCount)
391 : {
392 0 : if (m_cb->write != nullptr)
393 : {
394 0 : return m_cb->write(pFile, psBuffer, nSize, nCount);
395 : }
396 0 : CPLError(CE_Failure, CPLE_AppDefined, "Write not implemented for %s plugin",
397 : m_Prefix);
398 0 : return -1;
399 : }
400 :
401 0 : int VSIPluginFilesystemHandler::Flush(void *pFile)
402 : {
403 0 : if (m_cb->flush != nullptr)
404 : {
405 0 : return m_cb->flush(pFile);
406 : }
407 0 : CPLError(CE_Failure, CPLE_AppDefined, "Flush not implemented for %s plugin",
408 : m_Prefix);
409 0 : return -1;
410 : }
411 :
412 0 : int VSIPluginFilesystemHandler::Truncate(void *pFile, vsi_l_offset nNewSize)
413 : {
414 0 : if (m_cb->truncate != nullptr)
415 : {
416 0 : return m_cb->truncate(pFile, nNewSize);
417 : }
418 0 : CPLError(CE_Failure, CPLE_AppDefined,
419 : "Truncate not implemented for %s plugin", m_Prefix);
420 0 : return -1;
421 : }
422 :
423 0 : char **VSIPluginFilesystemHandler::ReadDirEx(const char *pszDirname,
424 : int nMaxFiles)
425 : {
426 0 : if (!IsValidFilename(pszDirname))
427 0 : return nullptr;
428 0 : if (m_cb->read_dir != nullptr)
429 : {
430 0 : return m_cb->read_dir(m_cb->pUserData, GetCallbackFilename(pszDirname),
431 0 : nMaxFiles);
432 : }
433 0 : return nullptr;
434 : }
435 :
436 0 : char **VSIPluginFilesystemHandler::SiblingFiles(const char *pszFilename)
437 : {
438 0 : if (!IsValidFilename(pszFilename))
439 0 : return nullptr;
440 0 : if (m_cb->sibling_files != nullptr)
441 : {
442 0 : return m_cb->sibling_files(m_cb->pUserData,
443 0 : GetCallbackFilename(pszFilename));
444 : }
445 0 : return nullptr;
446 : }
447 :
448 0 : int VSIPluginFilesystemHandler::Unlink(const char *pszFilename)
449 : {
450 0 : if (m_cb->unlink == nullptr || !IsValidFilename(pszFilename))
451 0 : return -1;
452 0 : return unlink(GetCallbackFilename(pszFilename));
453 : }
454 :
455 0 : int VSIPluginFilesystemHandler::Rename(const char *oldpath, const char *newpath)
456 : {
457 0 : if (m_cb->rename == nullptr || !IsValidFilename(oldpath) ||
458 0 : !IsValidFilename(newpath))
459 0 : return -1;
460 0 : return m_cb->rename(m_cb->pUserData, GetCallbackFilename(oldpath),
461 0 : GetCallbackFilename(newpath));
462 : }
463 :
464 0 : int VSIPluginFilesystemHandler::Mkdir(const char *pszDirname, long nMode)
465 : {
466 0 : if (m_cb->mkdir == nullptr || !IsValidFilename(pszDirname))
467 0 : return -1;
468 0 : return m_cb->mkdir(m_cb->pUserData, GetCallbackFilename(pszDirname), nMode);
469 : }
470 :
471 0 : int VSIPluginFilesystemHandler::Rmdir(const char *pszDirname)
472 : {
473 0 : if (m_cb->rmdir == nullptr || !IsValidFilename(pszDirname))
474 0 : return -1;
475 0 : return m_cb->rmdir(m_cb->pUserData, GetCallbackFilename(pszDirname));
476 : }
477 : } // namespace cpl
478 :
479 : #endif // DOXYGEN_SKIP
480 : //! @endcond
481 :
482 2 : int VSIInstallPluginHandler(const char *pszPrefix,
483 : const VSIFilesystemPluginCallbacksStruct *poCb)
484 : {
485 : VSIFilesystemHandler *poHandler =
486 2 : new cpl::VSIPluginFilesystemHandler(pszPrefix, poCb);
487 : // TODO: check pszPrefix starts and ends with a /
488 2 : VSIFileManager::InstallHandler(pszPrefix, poHandler);
489 2 : return 0;
490 : }
491 :
492 3 : int VSIRemovePluginHandler(const char *pszPrefix)
493 : {
494 3 : VSIFileManager::RemoveHandler(pszPrefix);
495 3 : return 0;
496 : }
497 :
498 : VSIFilesystemPluginCallbacksStruct *
499 2 : VSIAllocFilesystemPluginCallbacksStruct(void)
500 : {
501 : return static_cast<VSIFilesystemPluginCallbacksStruct *>(
502 2 : VSI_CALLOC_VERBOSE(1, sizeof(VSIFilesystemPluginCallbacksStruct)));
503 : }
504 :
505 2 : void VSIFreeFilesystemPluginCallbacksStruct(
506 : VSIFilesystemPluginCallbacksStruct *poCb)
507 : {
508 2 : CPLFree(poCb);
509 2 : }
|