1 /******************************************************************************
2 *
3 * Project: Virtual GDAL Datasets
4 * Purpose: Implementation of VRTRawRasterBand
5 * Author: Frank Warmerdam <warmerdam@pobox.com>
6 *
7 ******************************************************************************
8 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 * Copyright (c) 2007-2013, 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 "rawdataset.h"
32 #include "vrtdataset.h"
33
34 #include <cerrno>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38
39 #include "cpl_conv.h"
40 #include "cpl_error.h"
41 #include "cpl_hash_set.h"
42 #include "cpl_minixml.h"
43 #include "cpl_string.h"
44 #include "cpl_vsi.h"
45 #include "gdal.h"
46 #include "gdal_priv.h"
47
48 CPL_CVSID("$Id: vrtrawrasterband.cpp ef49c00611235df0c1ce4f51344f00567a668661 2020-02-13 11:08:39 +0100 Even Rouault $")
49
50 /*! @cond Doxygen_Suppress */
51
52 /************************************************************************/
53 /* ==================================================================== */
54 /* VRTRawRasterBand */
55 /* ==================================================================== */
56 /************************************************************************/
57
58 /************************************************************************/
59 /* VRTRawRasterBand() */
60 /************************************************************************/
61
VRTRawRasterBand(GDALDataset * poDSIn,int nBandIn,GDALDataType eType)62 VRTRawRasterBand::VRTRawRasterBand( GDALDataset *poDSIn, int nBandIn,
63 GDALDataType eType ) :
64 m_poRawRaster(nullptr),
65 m_pszSourceFilename(nullptr),
66 m_bRelativeToVRT(FALSE)
67 {
68 Initialize( poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize() );
69
70 // Declared in GDALRasterBand.
71 poDS = poDSIn;
72 nBand = nBandIn;
73
74 if( eType != GDT_Unknown )
75 eDataType = eType;
76 }
77
78 /************************************************************************/
79 /* ~VRTRawRasterBand() */
80 /************************************************************************/
81
~VRTRawRasterBand()82 VRTRawRasterBand::~VRTRawRasterBand()
83
84 {
85 FlushCache();
86 ClearRawLink();
87 }
88
89 /************************************************************************/
90 /* IRasterIO() */
91 /************************************************************************/
92
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)93 CPLErr VRTRawRasterBand::IRasterIO( GDALRWFlag eRWFlag,
94 int nXOff, int nYOff,
95 int nXSize, int nYSize,
96 void * pData, int nBufXSize, int nBufYSize,
97 GDALDataType eBufType,
98 GSpacing nPixelSpace,
99 GSpacing nLineSpace,
100 GDALRasterIOExtraArg* psExtraArg )
101 {
102 if( m_poRawRaster == nullptr )
103 {
104 CPLError( CE_Failure, CPLE_AppDefined,
105 "No raw raster band configured on VRTRawRasterBand." );
106 return CE_Failure;
107 }
108
109 if( eRWFlag == GF_Write && eAccess == GA_ReadOnly )
110 {
111 CPLError( CE_Failure, CPLE_NoWriteAccess,
112 "Attempt to write to read only dataset in"
113 "VRTRawRasterBand::IRasterIO()." );
114
115 return CE_Failure;
116 }
117
118 /* -------------------------------------------------------------------- */
119 /* Do we have overviews that would be appropriate to satisfy */
120 /* this request? */
121 /* -------------------------------------------------------------------- */
122 if( (nBufXSize < nXSize || nBufYSize < nYSize)
123 && GetOverviewCount() > 0 )
124 {
125 if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
126 pData, nBufXSize, nBufYSize,
127 eBufType, nPixelSpace,
128 nLineSpace, psExtraArg ) == CE_None )
129 return CE_None;
130 }
131
132 m_poRawRaster->SetAccess(eAccess);
133
134 return m_poRawRaster->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
135 pData, nBufXSize, nBufYSize,
136 eBufType, nPixelSpace,
137 nLineSpace, psExtraArg );
138 }
139
140 /************************************************************************/
141 /* IReadBlock() */
142 /************************************************************************/
143
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)144 CPLErr VRTRawRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
145 void * pImage )
146
147 {
148 if( m_poRawRaster == nullptr )
149 {
150 CPLError( CE_Failure, CPLE_AppDefined,
151 "No raw raster band configured on VRTRawRasterBand." );
152 return CE_Failure;
153 }
154
155 return m_poRawRaster->ReadBlock( nBlockXOff, nBlockYOff, pImage );
156 }
157
158 /************************************************************************/
159 /* IWriteBlock() */
160 /************************************************************************/
161
IWriteBlock(int nBlockXOff,int nBlockYOff,void * pImage)162 CPLErr VRTRawRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
163 void * pImage )
164
165 {
166 if( m_poRawRaster == nullptr )
167 {
168 CPLError( CE_Failure, CPLE_AppDefined,
169 "No raw raster band configured on VRTRawRasterBand." );
170 return CE_Failure;
171 }
172
173 m_poRawRaster->SetAccess(eAccess);
174
175 return m_poRawRaster->WriteBlock( nBlockXOff, nBlockYOff, pImage );
176 }
177
178 /************************************************************************/
179 /* SetRawLink() */
180 /************************************************************************/
181
SetRawLink(const char * pszFilename,const char * pszVRTPath,int bRelativeToVRTIn,vsi_l_offset nImageOffset,int nPixelOffset,int nLineOffset,const char * pszByteOrder)182 CPLErr VRTRawRasterBand::SetRawLink( const char *pszFilename,
183 const char *pszVRTPath,
184 int bRelativeToVRTIn,
185 vsi_l_offset nImageOffset,
186 int nPixelOffset, int nLineOffset,
187 const char *pszByteOrder )
188
189 {
190 ClearRawLink();
191
192 static_cast<VRTDataset *>( poDS )->SetNeedsFlush();
193
194 /* -------------------------------------------------------------------- */
195 /* Prepare filename. */
196 /* -------------------------------------------------------------------- */
197 if( pszFilename == nullptr )
198 {
199 CPLError( CE_Warning, CPLE_AppDefined,
200 "Missing <SourceFilename> element in VRTRasterBand." );
201 return CE_Failure;
202 }
203
204 char *pszExpandedFilename = nullptr;
205 if( pszVRTPath != nullptr && bRelativeToVRTIn )
206 {
207 pszExpandedFilename = CPLStrdup(
208 CPLProjectRelativeFilename( pszVRTPath, pszFilename ) );
209 }
210 else
211 {
212 pszExpandedFilename = CPLStrdup( pszFilename );
213 }
214
215 /* -------------------------------------------------------------------- */
216 /* Try and open the file. We always use the large file API. */
217 /* -------------------------------------------------------------------- */
218 CPLPushErrorHandler(CPLQuietErrorHandler);
219 FILE *fp = CPLOpenShared( pszExpandedFilename, "rb+", TRUE );
220
221 if( fp == nullptr )
222 fp = CPLOpenShared( pszExpandedFilename, "rb", TRUE );
223
224 if( fp == nullptr
225 && static_cast<VRTDataset *>( poDS )->GetAccess() == GA_Update )
226 {
227 fp = CPLOpenShared( pszExpandedFilename, "wb+", TRUE );
228 }
229 CPLPopErrorHandler();
230 CPLErrorReset();
231
232 if( fp == nullptr )
233 {
234 CPLError( CE_Failure, CPLE_OpenFailed,
235 "Unable to open %s.%s",
236 pszExpandedFilename, VSIStrerror( errno ) );
237
238 CPLFree( pszExpandedFilename );
239 return CE_Failure;
240 }
241
242 CPLFree( pszExpandedFilename );
243
244 if( !RAWDatasetCheckMemoryUsage(
245 nRasterXSize, nRasterYSize, 1,
246 GDALGetDataTypeSizeBytes(GetRasterDataType()),
247 nPixelOffset, nLineOffset, nImageOffset, 0,
248 reinterpret_cast<VSILFILE*>(fp)) )
249 {
250 CPLCloseShared(fp);
251 return CE_Failure;
252 }
253
254 m_pszSourceFilename = CPLStrdup(pszFilename);
255 m_bRelativeToVRT = bRelativeToVRTIn;
256
257 /* -------------------------------------------------------------------- */
258 /* Work out if we are in native mode or not. */
259 /* -------------------------------------------------------------------- */
260 RawRasterBand::ByteOrder eByteOrder =
261 #if CPL_IS_LSB
262 RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
263 #else
264 RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
265 #endif
266
267 if( pszByteOrder != nullptr )
268 {
269 if( EQUAL(pszByteOrder,"LSB") )
270 eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
271 else if( EQUAL(pszByteOrder,"MSB") )
272 eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
273 else if( EQUAL(pszByteOrder,"VAX") )
274 eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
275 else
276 {
277 CPLError( CE_Failure, CPLE_AppDefined,
278 "Illegal ByteOrder value '%s', should be LSB, MSB or VAX.",
279 pszByteOrder );
280 CPLCloseShared(fp);
281 return CE_Failure;
282 }
283 }
284
285 /* -------------------------------------------------------------------- */
286 /* Create a corresponding RawRasterBand. */
287 /* -------------------------------------------------------------------- */
288 m_poRawRaster = new RawRasterBand( reinterpret_cast<VSILFILE*>(fp),
289 nImageOffset, nPixelOffset,
290 nLineOffset, GetRasterDataType(),
291 eByteOrder, GetXSize(), GetYSize(),
292 RawRasterBand::OwnFP::NO );
293
294 /* -------------------------------------------------------------------- */
295 /* Reset block size to match the raw raster. */
296 /* -------------------------------------------------------------------- */
297 m_poRawRaster->GetBlockSize( &nBlockXSize, &nBlockYSize );
298
299 return CE_None;
300 }
301
302 /************************************************************************/
303 /* ClearRawLink() */
304 /************************************************************************/
305
ClearRawLink()306 void VRTRawRasterBand::ClearRawLink()
307
308 {
309 if( m_poRawRaster != nullptr )
310 {
311 VSILFILE* fp = m_poRawRaster->GetFPL();
312 delete m_poRawRaster;
313 m_poRawRaster = nullptr;
314 // We close the file after deleting the raster band
315 // since data can be flushed in the destructor.
316 if( fp != nullptr )
317 {
318 CPLCloseShared( reinterpret_cast<FILE*>( fp ) );
319 }
320 }
321 CPLFree( m_pszSourceFilename );
322 m_pszSourceFilename = nullptr;
323 }
324
325 /************************************************************************/
326 /* GetVirtualMemAuto() */
327 /************************************************************************/
328
GetVirtualMemAuto(GDALRWFlag eRWFlag,int * pnPixelSpace,GIntBig * pnLineSpace,char ** papszOptions)329 CPLVirtualMem * VRTRawRasterBand::GetVirtualMemAuto( GDALRWFlag eRWFlag,
330 int *pnPixelSpace,
331 GIntBig *pnLineSpace,
332 char **papszOptions )
333
334 {
335 // check the pointer to RawRasterBand
336 if( m_poRawRaster == nullptr )
337 {
338 // use the super class method
339 return VRTRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace, pnLineSpace, papszOptions);
340 }
341 // if available, use the RawRasterBand method (use mmap if available)
342 return m_poRawRaster->GetVirtualMemAuto(eRWFlag, pnPixelSpace, pnLineSpace, papszOptions);
343 }
344
345
346 /************************************************************************/
347 /* XMLInit() */
348 /************************************************************************/
349
XMLInit(CPLXMLNode * psTree,const char * pszVRTPath,void * pUniqueHandle,std::map<CPLString,GDALDataset * > & oMapSharedSources)350 CPLErr VRTRawRasterBand::XMLInit( CPLXMLNode * psTree,
351 const char *pszVRTPath,
352 void* pUniqueHandle,
353 std::map<CPLString, GDALDataset*>& oMapSharedSources )
354
355 {
356 const CPLErr eErr = VRTRasterBand::XMLInit( psTree, pszVRTPath, pUniqueHandle,
357 oMapSharedSources );
358 if( eErr != CE_None )
359 return eErr;
360
361 /* -------------------------------------------------------------------- */
362 /* Validate a bit. */
363 /* -------------------------------------------------------------------- */
364 if( psTree == nullptr || psTree->eType != CXT_Element
365 || !EQUAL(psTree->pszValue, "VRTRasterBand")
366 || !EQUAL(CPLGetXMLValue(psTree,"subClass",""), "VRTRawRasterBand") )
367 {
368 CPLError( CE_Failure, CPLE_AppDefined,
369 "Invalid node passed to VRTRawRasterBand::XMLInit()." );
370 return CE_Failure;
371 }
372
373 /* -------------------------------------------------------------------- */
374 /* Prepare filename. */
375 /* -------------------------------------------------------------------- */
376 const char *pszFilename =
377 CPLGetXMLValue(psTree, "SourceFilename", nullptr);
378
379 if( pszFilename == nullptr )
380 {
381 CPLError( CE_Warning, CPLE_AppDefined,
382 "Missing <SourceFilename> element in VRTRasterBand." );
383 return CE_Failure;
384 }
385
386 const bool l_bRelativeToVRT = CPLTestBool(
387 CPLGetXMLValue( psTree, "SourceFilename.relativeToVRT", "1" ));
388
389 /* -------------------------------------------------------------------- */
390 /* Collect layout information. */
391 /* -------------------------------------------------------------------- */
392 int nWordDataSize = GDALGetDataTypeSizeBytes( GetRasterDataType() );
393
394 const char* pszImageOffset = CPLGetXMLValue( psTree, "ImageOffset", "0");
395 const vsi_l_offset nImageOffset = CPLScanUIntBig(
396 pszImageOffset, static_cast<int>(strlen(pszImageOffset)) );
397
398 int nPixelOffset = nWordDataSize;
399 const char* pszPixelOffset =
400 CPLGetXMLValue( psTree, "PixelOffset", nullptr );
401 if( pszPixelOffset != nullptr )
402 {
403 nPixelOffset = atoi(pszPixelOffset);
404 }
405 if (nPixelOffset <= 0)
406 {
407 CPLError( CE_Failure, CPLE_AppDefined,
408 "Invalid value for <PixelOffset> element : %d",
409 nPixelOffset );
410 return CE_Failure;
411 }
412
413 int nLineOffset = 0;
414 const char* pszLineOffset = CPLGetXMLValue( psTree, "LineOffset", nullptr );
415 if( pszLineOffset == nullptr )
416 {
417 if( nPixelOffset > INT_MAX / GetXSize() )
418 {
419 CPLError( CE_Failure, CPLE_AppDefined, "Int overflow");
420 return CE_Failure;
421 }
422 nLineOffset = nPixelOffset * GetXSize();
423 }
424 else
425 nLineOffset = atoi(pszLineOffset);
426
427 const char *pszByteOrder = CPLGetXMLValue( psTree, "ByteOrder", nullptr );
428
429 /* -------------------------------------------------------------------- */
430 /* Open the file, and setup the raw layer access to the data. */
431 /* -------------------------------------------------------------------- */
432 return SetRawLink( pszFilename, pszVRTPath, l_bRelativeToVRT,
433 nImageOffset, nPixelOffset, nLineOffset,
434 pszByteOrder );
435 }
436
437 /************************************************************************/
438 /* SerializeToXML() */
439 /************************************************************************/
440
SerializeToXML(const char * pszVRTPath)441 CPLXMLNode *VRTRawRasterBand::SerializeToXML( const char *pszVRTPath )
442
443 {
444
445 /* -------------------------------------------------------------------- */
446 /* We can't set the layout if there is no open rawband. */
447 /* -------------------------------------------------------------------- */
448 if( m_poRawRaster == nullptr )
449 {
450 CPLError( CE_Failure, CPLE_AppDefined,
451 "VRTRawRasterBand::SerializeToXML() fails because "
452 "m_poRawRaster is NULL." );
453 return nullptr;
454 }
455
456 CPLXMLNode *psTree = VRTRasterBand::SerializeToXML( pszVRTPath );
457
458 /* -------------------------------------------------------------------- */
459 /* Set subclass. */
460 /* -------------------------------------------------------------------- */
461 CPLCreateXMLNode(
462 CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ),
463 CXT_Text, "VRTRawRasterBand" );
464
465 /* -------------------------------------------------------------------- */
466 /* Setup the filename with relative flag. */
467 /* -------------------------------------------------------------------- */
468 CPLXMLNode *psNode
469 = CPLCreateXMLElementAndValue( psTree, "SourceFilename",
470 m_pszSourceFilename );
471
472 CPLCreateXMLNode(
473 CPLCreateXMLNode( psNode, CXT_Attribute, "relativeToVRT" ),
474 CXT_Text, m_bRelativeToVRT ? "1" : "0" );
475
476 /* -------------------------------------------------------------------- */
477 /* Set other layout information. */
478 /* -------------------------------------------------------------------- */
479
480 CPLCreateXMLElementAndValue( psTree, "ImageOffset",
481 CPLSPrintf( CPL_FRMT_GUIB,
482 m_poRawRaster->GetImgOffset()) );
483
484 CPLCreateXMLElementAndValue( psTree, "PixelOffset",
485 CPLSPrintf( "%d",
486 m_poRawRaster->GetPixelOffset()) );
487
488 CPLCreateXMLElementAndValue( psTree, "LineOffset",
489 CPLSPrintf( "%d",
490 m_poRawRaster->GetLineOffset()) );
491
492 switch( m_poRawRaster->GetByteOrder() )
493 {
494 case RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN:
495 CPLCreateXMLElementAndValue( psTree, "ByteOrder", "LSB" );
496 break;
497 case RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN:
498 CPLCreateXMLElementAndValue( psTree, "ByteOrder", "MSB" );
499 break;
500 case RawRasterBand::ByteOrder::ORDER_VAX:
501 CPLCreateXMLElementAndValue( psTree, "ByteOrder", "VAX" );
502 break;
503 }
504
505 return psTree;
506 }
507
508 /************************************************************************/
509 /* GetFileList() */
510 /************************************************************************/
511
GetFileList(char *** ppapszFileList,int * pnSize,int * pnMaxSize,CPLHashSet * hSetFiles)512 void VRTRawRasterBand::GetFileList( char*** ppapszFileList, int *pnSize,
513 int *pnMaxSize, CPLHashSet* hSetFiles )
514 {
515 if (m_pszSourceFilename == nullptr)
516 return;
517
518 /* -------------------------------------------------------------------- */
519 /* Is it already in the list ? */
520 /* -------------------------------------------------------------------- */
521 CPLString osSourceFilename;
522 if( m_bRelativeToVRT && strlen(poDS->GetDescription()) > 0 )
523 osSourceFilename = CPLFormFilename(
524 CPLGetDirname(poDS->GetDescription()), m_pszSourceFilename, nullptr );
525 else
526 osSourceFilename = m_pszSourceFilename;
527
528 if( CPLHashSetLookup(hSetFiles, osSourceFilename) != nullptr )
529 return;
530
531 /* -------------------------------------------------------------------- */
532 /* Grow array if necessary */
533 /* -------------------------------------------------------------------- */
534 if (*pnSize + 1 >= *pnMaxSize)
535 {
536 *pnMaxSize = 2 + 2 * (*pnMaxSize);
537 *ppapszFileList = static_cast<char **>(
538 CPLRealloc( *ppapszFileList, sizeof(char*) * (*pnMaxSize) ) );
539 }
540
541 /* -------------------------------------------------------------------- */
542 /* Add the string to the list */
543 /* -------------------------------------------------------------------- */
544 (*ppapszFileList)[*pnSize] = CPLStrdup(osSourceFilename);
545 (*ppapszFileList)[(*pnSize + 1)] = nullptr;
546 CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]);
547
548 (*pnSize)++;
549
550 VRTRasterBand::GetFileList( ppapszFileList, pnSize,
551 pnMaxSize, hSetFiles);
552 }
553
554 /*! @endcond */
555