1 /******************************************************************************
2  * $Id: gdalopeninfo.cpp 29107 2015-05-02 11:23:26Z rouault $
3  *
4  * Project:  GDAL Core
5  * Purpose:  Implementation of GDALOpenInfo class.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  **********************************************************************
9  * Copyright (c) 2002, Frank Warmerdam
10  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at mines-paris dot org>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "gdal_priv.h"
32 #include "cpl_conv.h"
33 
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 
38 CPL_CVSID("$Id: gdalopeninfo.cpp 29107 2015-05-02 11:23:26Z rouault $");
39 
40 /************************************************************************/
41 /* ==================================================================== */
42 /*                             GDALOpenInfo                             */
43 /* ==================================================================== */
44 /************************************************************************/
45 
46 /************************************************************************/
47 /*                            GDALOpenInfo()                            */
48 /************************************************************************/
49 
GDALOpenInfo(const char * pszFilenameIn,int nOpenFlagsIn,char ** papszSiblingsIn)50 GDALOpenInfo::GDALOpenInfo( const char * pszFilenameIn, int nOpenFlagsIn,
51                             char **papszSiblingsIn )
52 
53 {
54 /* -------------------------------------------------------------------- */
55 /*      Ensure that C: is treated as C:\ so we can stat it on           */
56 /*      Windows.  Similar to what is done in CPLStat().                 */
57 /* -------------------------------------------------------------------- */
58 #ifdef WIN32
59     if( strlen(pszFilenameIn) == 2 && pszFilenameIn[1] == ':' )
60     {
61         char    szAltPath[10];
62 
63         strcpy( szAltPath, pszFilenameIn );
64         strcat( szAltPath, "\\" );
65         pszFilename = CPLStrdup( szAltPath );
66     }
67     else
68 #endif
69         pszFilename = CPLStrdup( pszFilenameIn );
70 
71 /* -------------------------------------------------------------------- */
72 /*      Initialize.                                                     */
73 /* -------------------------------------------------------------------- */
74 
75     nHeaderBytes = 0;
76     nHeaderBytesTried = 0;
77     pabyHeader = NULL;
78     bIsDirectory = FALSE;
79     bStatOK = FALSE;
80     nOpenFlags = nOpenFlagsIn;
81     eAccess = (nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly;
82     fpL = NULL;
83     papszOpenOptions = NULL;
84 
85 #ifdef HAVE_READLINK
86     int  bHasRetried = FALSE;
87 #endif
88 
89 /* -------------------------------------------------------------------- */
90 /*      Collect information about the file.                             */
91 /* -------------------------------------------------------------------- */
92     VSIStatBufL  sStat;
93 
94 #ifdef HAVE_READLINK
95 retry:
96 #endif
97     int bPotentialDirectory = FALSE;
98 
99     /* Check if the filename might be a directory of a special virtual file system */
100     if( strncmp(pszFilename, "/vsizip/", strlen("/vsizip/")) == 0 ||
101         strncmp(pszFilename, "/vsitar/", strlen("/vsitar/")) == 0 )
102     {
103         const char* pszExt = CPLGetExtension(pszFilename);
104         if( EQUAL(pszExt, "zip") || EQUAL(pszExt, "tar") || EQUAL(pszExt, "gz") )
105             bPotentialDirectory = TRUE;
106     }
107     else if( strncmp(pszFilename, "/vsicurl/", strlen("/vsicurl/")) == 0 )
108     {
109         bPotentialDirectory = TRUE;
110     }
111 
112     if( bPotentialDirectory )
113     {
114         /* For those special files, opening them with VSIFOpenL() might result */
115         /* in content, even if they should be considered as directories, so */
116         /* use stat */
117         if( VSIStatExL( pszFilename, &sStat,
118                         VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 )
119         {
120             bStatOK = TRUE;
121             if( VSI_ISDIR( sStat.st_mode ) )
122                 bIsDirectory = TRUE;
123         }
124     }
125 
126     if( !bIsDirectory )
127         fpL = VSIFOpenL( pszFilename, (eAccess == GA_Update) ? "r+b" : "rb" );
128     if( fpL != NULL )
129     {
130         bStatOK = TRUE;
131         pabyHeader = (GByte *) CPLCalloc(1025,1);
132         nHeaderBytesTried = 1024;
133         nHeaderBytes = (int) VSIFReadL( pabyHeader, 1, nHeaderBytesTried, fpL );
134         VSIRewindL( fpL );
135 
136         /* If we cannot read anything, check if it is not a directory instead */
137         if( nHeaderBytes == 0 &&
138             VSIStatExL( pszFilename, &sStat,
139                         VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 &&
140             VSI_ISDIR( sStat.st_mode ) )
141         {
142             VSIFCloseL(fpL);
143             fpL = NULL;
144             CPLFree(pabyHeader);
145             pabyHeader = NULL;
146             bIsDirectory = TRUE;
147         }
148     }
149     else if( !bStatOK )
150     {
151         if( VSIStatExL( pszFilename, &sStat,
152                         VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 )
153         {
154             bStatOK = TRUE;
155             if( VSI_ISDIR( sStat.st_mode ) )
156                 bIsDirectory = TRUE;
157         }
158 #ifdef HAVE_READLINK
159         else if ( !bHasRetried && strncmp(pszFilename, "/vsi", strlen("/vsi")) != 0 )
160         {
161             /* If someone creates a file with "ln -sf /vsicurl/http://download.osgeo.org/gdal/data/gtiff/utm.tif my_remote_utm.tif" */
162             /* we will be able to open it by passing my_remote_utm.tif */
163             /* This helps a lot for GDAL based readers that only provide file explorers to open datasets */
164             char szPointerFilename[2048];
165             int nBytes = readlink(pszFilename, szPointerFilename, sizeof(szPointerFilename));
166             if (nBytes != -1)
167             {
168                 szPointerFilename[MIN(nBytes, (int)sizeof(szPointerFilename)-1)] = 0;
169                 CPLFree(pszFilename);
170                 pszFilename = CPLStrdup(szPointerFilename);
171                 papszSiblingsIn = NULL;
172                 bHasRetried = TRUE;
173                 goto retry;
174             }
175         }
176 #endif
177     }
178 
179 /* -------------------------------------------------------------------- */
180 /*      Capture sibling list either from passed in values, or by        */
181 /*      scanning for them only if requested through GetSiblingFiles().  */
182 /* -------------------------------------------------------------------- */
183     if( papszSiblingsIn != NULL )
184     {
185         papszSiblingFiles = CSLDuplicate( papszSiblingsIn );
186         bHasGotSiblingFiles = TRUE;
187     }
188     else if( bStatOK && !bIsDirectory )
189     {
190         const char* pszOptionVal =
191             CPLGetConfigOption( "GDAL_DISABLE_READDIR_ON_OPEN", "NO" );
192         if (EQUAL(pszOptionVal, "EMPTY_DIR"))
193         {
194             papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) );
195             bHasGotSiblingFiles = TRUE;
196         }
197         else if( CSLTestBoolean(pszOptionVal) )
198         {
199             /* skip reading the directory */
200             papszSiblingFiles = NULL;
201             bHasGotSiblingFiles = TRUE;
202         }
203         else
204         {
205             /* will be lazy loaded */
206             papszSiblingFiles = NULL;
207             bHasGotSiblingFiles = FALSE;
208         }
209     }
210     else
211     {
212         papszSiblingFiles = NULL;
213         bHasGotSiblingFiles = TRUE;
214     }
215 }
216 
217 /************************************************************************/
218 /*                           ~GDALOpenInfo()                            */
219 /************************************************************************/
220 
~GDALOpenInfo()221 GDALOpenInfo::~GDALOpenInfo()
222 
223 {
224     VSIFree( pabyHeader );
225     CPLFree( pszFilename );
226 
227     if( fpL != NULL )
228         VSIFCloseL( fpL );
229     CSLDestroy( papszSiblingFiles );
230 }
231 
232 /************************************************************************/
233 /*                         GetSiblingFiles()                            */
234 /************************************************************************/
235 
GetSiblingFiles()236 char** GDALOpenInfo::GetSiblingFiles()
237 {
238     if( bHasGotSiblingFiles )
239         return papszSiblingFiles;
240     bHasGotSiblingFiles = TRUE;
241 
242     CPLString osDir = CPLGetDirname( pszFilename );
243     papszSiblingFiles = VSIReadDir( osDir );
244 
245     /* Small optimization to avoid unnecessary stat'ing from PAux or ENVI */
246     /* drivers. The MBTiles driver needs no companion file. */
247     if( papszSiblingFiles == NULL &&
248         strncmp(pszFilename, "/vsicurl/", 9) == 0 &&
249         EQUAL(CPLGetExtension( pszFilename ),"mbtiles") )
250     {
251         papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) );
252     }
253 
254     return papszSiblingFiles;
255 }
256 
257 
258 /************************************************************************/
259 /*                           TryToIngest()                              */
260 /************************************************************************/
261 
TryToIngest(int nBytes)262 int GDALOpenInfo::TryToIngest(int nBytes)
263 {
264     if( fpL == NULL )
265         return FALSE;
266     if( nHeaderBytes < nHeaderBytesTried )
267         return TRUE;
268     pabyHeader = (GByte*) CPLRealloc(pabyHeader, nBytes + 1);
269     memset(pabyHeader, 0, nBytes + 1);
270     VSIRewindL(fpL);
271     nHeaderBytesTried = nBytes;
272     nHeaderBytes = (int) VSIFReadL(pabyHeader, 1, nBytes, fpL);
273     VSIRewindL(fpL);
274 
275     return TRUE;
276 }
277