Coverage Report

Created: 2025-06-13 06:18

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