Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdriver.cpp
Line
Count
Source
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
159k
{
52
159k
    if (!poOpenInfo->bStatOK)
53
44.8k
    {
54
        // Are we being passed the XML definition directly?
55
        // Skip any leading spaces/blanks.
56
44.8k
        const char *pszTestXML = poOpenInfo->pszFilename;
57
44.8k
        while (*pszTestXML != '\0' &&
58
44.8k
               isspace(static_cast<unsigned char>(*pszTestXML)))
59
3
            pszTestXML++;
60
44.8k
        if (STARTS_WITH_CI(pszTestXML, "<OGRVRTDataSource>"))
61
0
        {
62
0
            return TRUE;
63
0
        }
64
44.8k
        return FALSE;
65
44.8k
    }
66
67
115k
    return poOpenInfo->fpL != nullptr &&
68
108k
           strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
69
108k
                  "<OGRVRTDataSource") != nullptr;
70
159k
}
71
72
/************************************************************************/
73
/*                                Open()                                */
74
/************************************************************************/
75
76
static GDALDataset *OGRVRTDriverOpen(GDALOpenInfo *poOpenInfo)
77
78
25.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
25.8k
    const char *pszTestXML = poOpenInfo->pszFilename;
87
25.8k
    while (*pszTestXML != '\0' &&
88
25.8k
           isspace(static_cast<unsigned char>(*pszTestXML)))
89
0
        pszTestXML++;
90
91
25.8k
    char *pszXML = nullptr;
92
25.8k
    if (STARTS_WITH_CI(pszTestXML, "<OGRVRTDataSource>"))
93
0
    {
94
0
        pszXML = CPLStrdup(pszTestXML);
95
0
    }
96
97
    // Open file and check if it contains appropriate XML.
98
25.8k
    else
99
25.8k
    {
100
25.8k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
101
25.8k
        if (poOpenInfo->fpL == nullptr)
102
0
            return nullptr;
103
25.8k
#endif
104
25.8k
        VSIStatBufL sStatBuf;
105
25.8k
        if (VSIStatL(poOpenInfo->pszFilename, &sStatBuf) != 0)
106
0
        {
107
0
            return nullptr;
108
0
        }
109
25.8k
        if (sStatBuf.st_size > 10 * 1024 * 1024 &&
110
3
            !CPLTestBool(CPLGetConfigOption("OGR_VRT_FORCE_LOADING", "NO")))
111
3
        {
112
3
            CPLError(CE_Failure, CPLE_AppDefined,
113
3
                     "Suscipicously long VRT file. If you really want to "
114
3
                     "open it, define OGR_VRT_FORCE_LOADING=YES as "
115
3
                     "configuration option");
116
3
            return nullptr;
117
3
        }
118
25.8k
        if (static_cast<uint64_t>(sStatBuf.st_size) >
119
25.8k
            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
25.8k
        const size_t nLen = static_cast<size_t>(sStatBuf.st_size);
126
127
25.8k
        pszXML = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen + 1));
128
25.8k
        if (pszXML == nullptr)
129
0
            return nullptr;
130
131
25.8k
        pszXML[nLen] = '\0';
132
25.8k
        VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
133
25.8k
        if (VSIFReadL(pszXML, 1, nLen, poOpenInfo->fpL) != nLen)
134
0
        {
135
0
            CPLFree(pszXML);
136
0
            return nullptr;
137
0
        }
138
25.8k
        VSIFCloseL(poOpenInfo->fpL);
139
25.8k
        poOpenInfo->fpL = nullptr;
140
25.8k
    }
141
142
    // Parse the XML.
143
25.8k
    CPLXMLNode *psTree = CPLParseXMLString(pszXML);
144
145
25.8k
    if (psTree == nullptr)
146
3.47k
    {
147
3.47k
        CPLFree(pszXML);
148
3.47k
        return nullptr;
149
3.47k
    }
150
151
    // XML Validation.
152
22.3k
    if (CPLTestBool(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")))
153
22.3k
    {
154
22.3k
        const char *pszXSD = CPLFindFile("gdal", "ogrvrt.xsd");
155
22.3k
        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
22.3k
    }
177
22.3k
    CPLFree(pszXML);
178
179
    // Create a virtual datasource configured based on this XML input.
180
22.3k
    OGRVRTDataSource *poDS = new OGRVRTDataSource(
181
22.3k
        static_cast<GDALDriver *>(GDALGetDriverByName("OGR_VRT")));
182
183
    // psTree is owned by poDS.
184
22.3k
    if (!poDS->Initialize(psTree, poOpenInfo->pszFilename,
185
22.3k
                          poOpenInfo->eAccess == GA_Update))
186
65
    {
187
65
        delete poDS;
188
65
        return nullptr;
189
65
    }
190
191
22.3k
    return poDS;
192
22.3k
}
193
194
/************************************************************************/
195
/*                           RegisterOGRVRT()                           */
196
/************************************************************************/
197
198
void RegisterOGRVRT()
199
200
22
{
201
22
    if (GDALGetDriverByName("OGR_VRT") != nullptr)
202
0
        return;
203
204
22
    GDALDriver *poDriver = new GDALDriver();
205
206
22
    poDriver->SetDescription("OGR_VRT");
207
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
208
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "VRT - Virtual Datasource");
209
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "vrt");
210
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/vrt.html");
211
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
212
22
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES, "YES");
213
22
    poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_READ, "YES");
214
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
215
22
    poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
216
22
    poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
217
22
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
218
22
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
219
220
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
221
22
                              "WidthPrecision Nullable Unique Default "
222
22
                              "Comment AlternativeName");
223
224
22
    poDriver->pfnOpen = OGRVRTDriverOpen;
225
22
    poDriver->pfnIdentify = OGRVRTDriverIdentify;
226
227
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
228
22
}