GDALDriver * GDALDriverManager::GetDriver( int iDriver )
{
CPLMutexHolderD( &hDMMutex );
return GetDriver_unlocked(iDriver);
}
/************************************************************************/
/* GDALOpenEx() */
/************************************************************************/
/**
* \brief Open a raster or vector file as a GDALDataset.
*
* This function will try to open the passed file, or virtual dataset
* name by invoking the Open method of each registered GDALDriver in turn.
* The first successful open will result in a returned dataset. If all
* drivers fail then NULL is returned and an error is issued.
*
* Several recommendations :
* <ul>
* <li>If you open a dataset object with GDAL_OF_UPDATE access, it is not
* recommended to open a new dataset on the same underlying file.</li>
* <li>The returned dataset should only be accessed by one thread at a time. If
* you want to use it from different threads, you must add all necessary code
* (mutexes, etc.) to avoid concurrent use of the object. (Some drivers, such
* as GeoTIFF, maintain internal state variables that are updated each time a
* new block is read, thus preventing concurrent use.) </li>
* </ul>
*
* For drivers supporting the VSI virtual file API, it is possible to open a
* file in a .zip archive (see VSIInstallZipFileHandler()), in a
* .tar/.tar.gz/.tgz archive (see VSIInstallTarFileHandler()) or on a HTTP / FTP
* server (see VSIInstallCurlFileHandler())
*
* In some situations (dealing with unverified data), the datasets can be opened
* in another process through the \ref gdal_api_proxy mechanism.
*
* In order to reduce the need for searches through the operating system
* file system machinery, it is possible to give an optional list of files with
* the papszSiblingFiles parameter.
* This is the list of all files at the same level in the file system as the
* target file, including the target file. The filenames must not include any
* path components, are essentially just the output of VSIReadDir() on the
* parent directory. If the target object does not have filesystem semantics
* then the file list should be NULL.
*
* @param pszFilename the name of the file to access. In the case of
* exotic drivers this may not refer to a physical file, but instead contain
* information for the driver on how to access a dataset. It should be in UTF-8
* encoding.
*
* @param nOpenFlags a combination of GDAL_OF_ flags that may be combined
* through logical or operator.
* <ul>
* <li>Driver kind: GDAL_OF_RASTER for raster drivers, GDAL_OF_VECTOR for vector
* drivers. If none of the value is specified, both kinds are implied.</li>
* <li>Access mode: GDAL_OF_READONLY (exclusive)or GDAL_OF_UPDATE.</li>
* <li>Shared mode: GDAL_OF_SHARED. If set, it allows the sharing of GDALDataset
* handles for a dataset with other callers that have set GDAL_OF_SHARED.
* In particular, GDALOpenEx() will first consult its list of currently
* open and shared GDALDataset's, and if the GetDescription() name for one
* exactly matches the pszFilename passed to GDALOpenEx() it will be
* referenced and returned, if GDALOpenEx() is called from the same thread.</li>
* <li>Verbose error: GDAL_OF_VERBOSE_ERROR. If set, a failed attempt to open
* the file will lead to an error message to be reported.</li>
* </ul>
*
* @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL
* terminated list of strings with the driver short names that must be
* considered.
*
* @param papszOpenOptions NULL, or a NULL terminated list of strings with open
* options passed to candidate drivers. An option exists for all drivers,
* OVERVIEW_LEVEL=level, to select a particular overview level of a dataset.
* The level index starts at 0. The level number can be suffixed by "only" to
* specify that only this overview level must be visible, and not sub-levels.
* Open options are validated by default, and a warning is emitted in case the
* option is not recognized. In some scenarios, it might be not desirable (e.g.
* when not knowing which driver will open the file), so the special open option
* VALIDATE_OPEN_OPTIONS can be set to NO to avoid such warnings. Alternatively,
* since GDAL 2.1, an option name can be preceded by the @ character to indicate
* that it may not cause a warning if the driver doesn't declare this option.
*
* @param papszSiblingFiles NULL, or a NULL terminated list of strings that are
* filenames that are auxiliary to the main filename. If NULL is passed, a
* probing of the file system will be done.
*
* @return A GDALDatasetH handle or NULL on failure. For C++ applications
* this handle can be cast to a GDALDataset *.
*
* @since GDAL 2.0
*/
GDALDatasetH CPL_STDCALL GDALOpenEx( const char *pszFilename,
unsigned int nOpenFlags,
const char *const *papszAllowedDrivers,
const char *const *papszOpenOptions,
const char *const *papszSiblingFiles )
{
VALIDATE_POINTER1(pszFilename, "GDALOpen", nullptr);
/* -------------------------------------------------------------------- */
/* In case of shared dataset, first scan the existing list to see */
/* if it could already contain the requested dataset. */
/* -------------------------------------------------------------------- */
if( nOpenFlags & GDAL_OF_SHARED )
{
if( nOpenFlags & GDAL_OF_INTERNAL )
{
CPLError(CE_Failure, CPLE_IllegalArg,
"GDAL_OF_SHARED and GDAL_OF_INTERNAL are exclusive");
return nullptr;
}
CPLMutexHolderD(&hDLMutex);
if (phSharedDatasetSet != nullptr)
{
const GIntBig nThisPID = GDALGetResponsiblePIDForCurrentThread();
SharedDatasetCtxt sStruct;
sStruct.nPID = nThisPID;
sStruct.pszDescription = const_cast<char *>(pszFilename);
sStruct.eAccess =
(nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly;
SharedDatasetCtxt *psStruct = static_cast<SharedDatasetCtxt *>(
CPLHashSetLookup(phSharedDatasetSet, &sStruct));
if (psStruct == nullptr && (nOpenFlags & GDAL_OF_UPDATE) == 0)
{
sStruct.eAccess = GA_Update;
psStruct = static_cast<SharedDatasetCtxt *>(
CPLHashSetLookup(phSharedDatasetSet, &sStruct));
}
if (psStruct)
{
psStruct->poDS->Reference();
return psStruct->poDS;
}
}
}
// If no driver kind is specified, assume all are to be probed.
if( (nOpenFlags & GDAL_OF_KIND_MASK) == 0 )
nOpenFlags |= GDAL_OF_KIND_MASK;
GDALDriverManager *poDM = GetGDALDriverManager();
// CPLLocaleC oLocaleForcer;
CPLErrorReset();
VSIErrorReset();
CPLAssert(nullptr != poDM);
// Build GDALOpenInfo just now to avoid useless file stat'ing if a
// shared dataset was asked before.
GDALOpenInfo oOpenInfo(pszFilename, nOpenFlags,
const_cast<char **>(papszSiblingFiles));
oOpenInfo.papszAllowedDrivers = papszAllowedDrivers;
// Prevent infinite recursion.
{
int *pnRecCount =
static_cast<int *>(CPLGetTLS(CTLS_GDALDATASET_REC_PROTECT_MAP));
if( pnRecCount == nullptr )
{
pnRecCount = static_cast<int *>(CPLMalloc(sizeof(int)));
*pnRecCount = 0;
CPLSetTLS(CTLS_GDALDATASET_REC_PROTECT_MAP, pnRecCount, TRUE);
}
if( *pnRecCount == 100 )
{
CPLError(CE_Failure, CPLE_AppDefined,
"GDALOpen() called with too many recursion levels");
return nullptr;
}
(*pnRecCount)++;
}
// Remove leading @ if present.
char **papszOpenOptionsCleaned =
CSLDuplicate(const_cast<char **>(papszOpenOptions));
for(char **papszIter = papszOpenOptionsCleaned; papszIter && *papszIter;
++papszIter)
{
char *pszOption = *papszIter;
if( pszOption[0] == '@' )
memmove(pszOption, pszOption + 1, strlen(pszOption + 1) + 1);
}
oOpenInfo.papszOpenOptions = papszOpenOptionsCleaned;
for( int iDriver = -1; iDriver < poDM->GetDriverCount(); ++iDriver )
{
GDALDriver *poDriver = nullptr;
if( iDriver < 0 )
{
poDriver = GDALGetAPIPROXYDriver();
}
else
{
poDriver = poDM->GetDriver(iDriver);
if (papszAllowedDrivers != nullptr &&
CSLFindString(papszAllowedDrivers,
GDALGetDriverShortName(poDriver)) == -1)
continue;
}
if( (nOpenFlags & GDAL_OF_RASTER) != 0 &&
(nOpenFlags & GDAL_OF_VECTOR) == 0 &&
poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr )
continue;
if( (nOpenFlags & GDAL_OF_VECTOR) != 0 &&
(nOpenFlags & GDAL_OF_RASTER) == 0 &&
poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr )
continue;
// Remove general OVERVIEW_LEVEL open options from list before passing
// it to the driver, if it isn't a driver specific option already.
char **papszTmpOpenOptions = nullptr;
char **papszTmpOpenOptionsToValidate = nullptr;
char **papszOptionsToValidate = const_cast<char **>(papszOpenOptions);
if( CSLFetchNameValue(papszOpenOptionsCleaned, "OVERVIEW_LEVEL") !=
nullptr &&
(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST) == nullptr ||
CPLString(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST))
.ifind("OVERVIEW_LEVEL") == std::string::npos) )
{
papszTmpOpenOptions = CSLDuplicate(papszOpenOptionsCleaned);
papszTmpOpenOptions =
CSLSetNameValue(papszTmpOpenOptions, "OVERVIEW_LEVEL", nullptr);
oOpenInfo.papszOpenOptions = papszTmpOpenOptions;
papszOptionsToValidate = CSLDuplicate(papszOptionsToValidate);
papszOptionsToValidate =
CSLSetNameValue(papszOptionsToValidate, "OVERVIEW_LEVEL", nullptr);
papszTmpOpenOptionsToValidate = papszOptionsToValidate;
}
const bool bIdentifyRes =
poDriver->pfnIdentify && poDriver->pfnIdentify(&oOpenInfo) > 0;
if( bIdentifyRes )
{
GDALValidateOpenOptions(poDriver, papszOptionsToValidate);
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
const bool bFpAvailableBefore = oOpenInfo.fpL != nullptr;
CPLErrorReset();
#endif
GDALDataset *poDS = nullptr;
if ( poDriver->pfnOpen != nullptr )
{
poDS = poDriver->pfnOpen(&oOpenInfo);
// If we couldn't determine for sure with Identify() (it returned
// -1), but Open() managed to open the file, post validate options.
if( poDS != nullptr && poDriver->pfnIdentify && !bIdentifyRes )
GDALValidateOpenOptions(poDriver, papszOptionsToValidate);
}
else if( poDriver->pfnOpenWithDriverArg != nullptr )
{
poDS = poDriver->pfnOpenWithDriverArg(poDriver, &oOpenInfo);
}
else
{
CSLDestroy(papszTmpOpenOptions);
CSLDestroy(papszTmpOpenOptionsToValidate);
oOpenInfo.papszOpenOptions = papszOpenOptionsCleaned;
continue;
}
CSLDestroy(papszTmpOpenOptions);
CSLDestroy(papszTmpOpenOptionsToValidate);
oOpenInfo.papszOpenOptions = papszOpenOptionsCleaned;
if( poDS != nullptr )
{
poDS->nOpenFlags = nOpenFlags;
if( strlen(poDS->GetDescription()) == 0 )
poDS->SetDescription(pszFilename);
if( poDS->poDriver == nullptr )
poDS->poDriver = poDriver;
if( poDS->papszOpenOptions == nullptr )
{
poDS->papszOpenOptions = papszOpenOptionsCleaned;
papszOpenOptionsCleaned = nullptr;
}
if( !(nOpenFlags & GDAL_OF_INTERNAL) )
{
if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() )
CPLDebug("GDAL",
"GDALOpen(%s, this=%p) succeeds as "
"%s (pid=%d, responsiblePID=%d).",
pszFilename, poDS, poDriver->GetDescription(),
static_cast<int>(CPLGetPID()),
static_cast<int>(
GDALGetResponsiblePIDForCurrentThread()));
else
CPLDebug("GDAL", "GDALOpen(%s, this=%p) succeeds as %s.",
pszFilename, poDS, poDriver->GetDescription());
poDS->AddToDatasetOpenList();
}
int *pnRecCount =
static_cast<int *>(CPLGetTLS(CTLS_GDALDATASET_REC_PROTECT_MAP));
if( pnRecCount )
(*pnRecCount)--;
if( nOpenFlags & GDAL_OF_SHARED )
{
if (strcmp(pszFilename, poDS->GetDescription()) != 0)
{
CPLError(CE_Warning, CPLE_NotSupported,
"A dataset opened by GDALOpenShared should have "
"the same filename (%s) "
"and description (%s)",
pszFilename, poDS->GetDescription());
}
else
{
poDS->MarkAsShared();
}
}
// Deal with generic OVERVIEW_LEVEL open option, unless it is
// driver specific.
if( CSLFetchNameValue(papszOpenOptions, "OVERVIEW_LEVEL") != nullptr &&
(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST) == nullptr ||
CPLString(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST))
.ifind("OVERVIEW_LEVEL") == std::string::npos) )
{
CPLString osVal(
CSLFetchNameValue(papszOpenOptions, "OVERVIEW_LEVEL"));
const int nOvrLevel = atoi(osVal);
const bool bThisLevelOnly =
osVal.ifind("only") != std::string::npos;
GDALDataset *poOvrDS = GDALCreateOverviewDataset(
poDS, nOvrLevel, bThisLevelOnly);
poDS->ReleaseRef();
poDS = poOvrDS;
if( poDS == nullptr )
{
if( nOpenFlags & GDAL_OF_VERBOSE_ERROR )
{
CPLError(CE_Failure, CPLE_OpenFailed,
"Cannot open overview level %d of %s",
nOvrLevel, pszFilename);
}
}
}
VSIErrorReset();
CSLDestroy(papszOpenOptionsCleaned);
return poDS;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if( bFpAvailableBefore && oOpenInfo.fpL == nullptr )
{
// In case the file descriptor was "consumed" by a driver
// that ultimately failed, re-open it for next drivers.
oOpenInfo.fpL = VSIFOpenL(
pszFilename,
(oOpenInfo.eAccess == GA_Update) ? "r+b" : "rb");
}
#else
if( CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() > CE_Warning)
{
int *pnRecCount =
static_cast<int *>(CPLGetTLS(CTLS_GDALDATASET_REC_PROTECT_MAP));
if( pnRecCount )
(*pnRecCount)--;
CSLDestroy(papszOpenOptionsCleaned);
return nullptr;
}
#endif
}
CSLDestroy(papszOpenOptionsCleaned);
if( nOpenFlags & GDAL_OF_VERBOSE_ERROR )
{
// Check to see if there was a filesystem error, and report it if so.
// If not, return a more generic error.
if(!VSIToCPLError(CE_Failure, CPLE_OpenFailed))
{
if( oOpenInfo.bStatOK )
{
CPLError(CE_Failure, CPLE_OpenFailed,
"`%s' not recognized as a supported file format.",
pszFilename);
}
else
{
// If Stat failed and no VSI error was set, assume it is because
// the file did not exist on the filesystem.
CPLError(CE_Failure, CPLE_OpenFailed,
"`%s' does not exist in the file system, "
"and is not recognized as a supported dataset name.",
pszFilename);
}
}
}
int *pnRecCount =
static_cast<int *>(CPLGetTLS(CTLS_GDALDATASET_REC_PROTECT_MAP));
if( pnRecCount )
(*pnRecCount)--;
return nullptr;
}