/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 | } |