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