Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdriver.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGRVRTDriver class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_vrt.h"
16
17
#include <cctype>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstring>
21
#include <limits>
22
#include <memory>
23
#include <vector>
24
25
#include "cpl_conv.h"
26
#include "cpl_error.h"
27
#include "cpl_minixml.h"
28
#include "cpl_string.h"
29
#include "cpl_vsi.h"
30
#include "gdal.h"
31
#include "gdal_priv.h"
32
33
/************************************************************************/
34
/*                           OGRVRTErrorHandler()                       */
35
/************************************************************************/
36
37
static void CPL_STDCALL OGRVRTErrorHandler(CPL_UNUSED CPLErr eErr,
38
                                           CPL_UNUSED CPLErrorNum nType,
39
                                           const char *pszMsg)
40
0
{
41
0
    std::vector<CPLString> *paosErrors =
42
0
        static_cast<std::vector<CPLString> *>(CPLGetErrorHandlerUserData());
43
0
    paosErrors->push_back(pszMsg);
44
0
}
45
46
/************************************************************************/
47
/*                         OGRVRTDriverIdentify()                       */
48
/************************************************************************/
49
50
static int OGRVRTDriverIdentify(GDALOpenInfo *poOpenInfo)
51
161k
{
52
161k
    if (!poOpenInfo->bStatOK)
53
49.9k
    {
54
        // Are we being passed the XML definition directly?
55
        // Skip any leading spaces/blanks.
56
49.9k
        const char *pszTestXML = poOpenInfo->pszFilename;
57
49.9k
        while (*pszTestXML != '\0' &&
58
49.9k
               isspace(static_cast<unsigned char>(*pszTestXML)))
59
24
            pszTestXML++;
60
49.9k
        if (STARTS_WITH_CI(pszTestXML, "<OGRVRTDataSource>"))
61
1
        {
62
1
            return TRUE;
63
1
        }
64
49.9k
        return FALSE;
65
49.9k
    }
66
67
111k
    return poOpenInfo->fpL != nullptr &&
68
111k
           strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
69
103k
                  "<OGRVRTDataSource") != nullptr;
70
161k
}
71
72
/************************************************************************/
73
/*                                Open()                                */
74
/************************************************************************/
75
76
static GDALDataset *OGRVRTDriverOpen(GDALOpenInfo *poOpenInfo)
77
78
17.8k
{
79
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
80
    if (!OGRVRTDriverIdentify(poOpenInfo))
81
        return nullptr;
82
#endif
83
84
    // Are we being passed the XML definition directly?
85
    // Skip any leading spaces/blanks.
86
17.8k
    const char *pszTestXML = poOpenInfo->pszFilename;
87
17.8k
    while (*pszTestXML != '\0' &&
88
17.8k
           isspace(static_cast<unsigned char>(*pszTestXML)))
89
1
        pszTestXML++;
90
91
17.8k
    char *pszXML = nullptr;
92
17.8k
    if (STARTS_WITH_CI(pszTestXML, "<OGRVRTDataSource>"))
93
1
    {
94
1
        pszXML = CPLStrdup(pszTestXML);
95
1
    }
96
97
    // Open file and check if it contains appropriate XML.
98
17.8k
    else
99
17.8k
    {
100
17.8k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
101
17.8k
        if (poOpenInfo->fpL == nullptr)
102
0
            return nullptr;
103
17.8k
#endif
104
17.8k
        VSIStatBufL sStatBuf;
105
17.8k
        if (VSIStatL(poOpenInfo->pszFilename, &sStatBuf) != 0)
106
0
        {
107
0
            return nullptr;
108
0
        }
109
17.8k
        if (sStatBuf.st_size > 10 * 1024 * 1024 &&
110
17.8k
            !CPLTestBool(CPLGetConfigOption("OGR_VRT_FORCE_LOADING", "NO")))
111
4
        {
112
4
            CPLError(CE_Failure, CPLE_AppDefined,
113
4
                     "Suscipicously long VRT file. If you really want to "
114
4
                     "open it, define OGR_VRT_FORCE_LOADING=YES as "
115
4
                     "configuration option");
116
4
            return nullptr;
117
4
        }
118
17.7k
        if (static_cast<uint64_t>(sStatBuf.st_size) >
119
17.7k
            std::numeric_limits<size_t>::max() - 1)
120
0
        {
121
0
            return nullptr;
122
0
        }
123
124
        // It is the right file, now load the full XML definition.
125
17.7k
        const size_t nLen = static_cast<size_t>(sStatBuf.st_size);
126
127
17.7k
        pszXML = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen + 1));
128
17.7k
        if (pszXML == nullptr)
129
0
            return nullptr;
130
131
17.7k
        pszXML[nLen] = '\0';
132
17.7k
        VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
133
17.7k
        if (VSIFReadL(pszXML, 1, nLen, poOpenInfo->fpL) != nLen)
134
0
        {
135
0
            CPLFree(pszXML);
136
0
            return nullptr;
137
0
        }
138
17.7k
        VSIFCloseL(poOpenInfo->fpL);
139
17.7k
        poOpenInfo->fpL = nullptr;
140
17.7k
    }
141
142
    // Parse the XML.
143
17.7k
    CPLXMLNode *psTree = CPLParseXMLString(pszXML);
144
145
17.7k
    if (psTree == nullptr)
146
3.23k
    {
147
3.23k
        CPLFree(pszXML);
148
3.23k
        return nullptr;
149
3.23k
    }
150
151
    // XML Validation.
152
14.5k
    if (CPLTestBool(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")))
153
14.5k
    {
154
14.5k
        const char *pszXSD = CPLFindFile("gdal", "ogrvrt.xsd");
155
14.5k
        if (pszXSD != nullptr)
156
0
        {
157
0
            std::vector<CPLString> aosErrors;
158
0
            CPLPushErrorHandlerEx(OGRVRTErrorHandler, &aosErrors);
159
0
            const int bRet = CPLValidateXML(pszXML, pszXSD, nullptr);
160
0
            CPLPopErrorHandler();
161
0
            if (!bRet)
162
0
            {
163
0
                if (!aosErrors.empty() &&
164
0
                    strstr(aosErrors[0].c_str(), "missing libxml2 support") ==
165
0
                        nullptr)
166
0
                {
167
0
                    for (size_t i = 0; i < aosErrors.size(); i++)
168
0
                    {
169
0
                        CPLError(CE_Warning, CPLE_AppDefined, "%s",
170
0
                                 aosErrors[i].c_str());
171
0
                    }
172
0
                }
173
0
            }
174
0
            CPLErrorReset();
175
0
        }
176
14.5k
    }
177
14.5k
    CPLFree(pszXML);
178
179
    // Create a virtual datasource configured based on this XML input.
180
14.5k
    OGRVRTDataSource *poDS = new OGRVRTDataSource(
181
14.5k
        static_cast<GDALDriver *>(GDALGetDriverByName("OGR_VRT")));
182
183
    // psTree is owned by poDS.
184
14.5k
    if (!poDS->Initialize(psTree, poOpenInfo->pszFilename,
185
14.5k
                          poOpenInfo->eAccess == GA_Update))
186
64
    {
187
64
        delete poDS;
188
64
        return nullptr;
189
64
    }
190
191
14.5k
    return poDS;
192
14.5k
}
193
194
/************************************************************************/
195
/*                           RegisterOGRVRT()                           */
196
/************************************************************************/
197
198
void RegisterOGRVRT()
199
200
24
{
201
24
    if (GDALGetDriverByName("OGR_VRT") != nullptr)
202
0
        return;
203
204
24
    GDALDriver *poDriver = new GDALDriver();
205
206
24
    poDriver->SetDescription("OGR_VRT");
207
24
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
208
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "VRT - Virtual Datasource");
209
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "vrt");
210
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/vrt.html");
211
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
212
24
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES, "YES");
213
24
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_READ, "YES");
214
24
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
215
24
    poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
216
24
    poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
217
24
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
218
24
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
219
220
24
    poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
221
24
                              "WidthPrecision Nullable Unique Default "
222
24
                              "Comment AlternativeName");
223
224
24
    poDriver->pfnOpen = OGRVRTDriverOpen;
225
24
    poDriver->pfnIdentify = OGRVRTDriverIdentify;
226
227
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
228
24
}