1 : /******************************************************************************
2 : * $Id: cpl_vsil_tar.cpp 21781 2011-02-21 21:57:41Z rouault $
3 : *
4 : * Project: CPL - Common Portability Library
5 : * Purpose: Implement VSI large file api for tar files (.tar).
6 : * Author: Even Rouault, even.rouault at mines-paris.org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_vsi_virtual.h"
31 :
32 : CPL_CVSID("$Id: cpl_vsil_tar.cpp 21781 2011-02-21 21:57:41Z rouault $");
33 :
34 :
35 : /************************************************************************/
36 : /* ==================================================================== */
37 : /* VSITarEntryFileOffset */
38 : /* ==================================================================== */
39 : /************************************************************************/
40 :
41 : class VSITarEntryFileOffset : public VSIArchiveEntryFileOffset
42 36 : {
43 : public:
44 : GUIntBig nOffset;
45 :
46 36 : VSITarEntryFileOffset(GUIntBig nOffset)
47 36 : {
48 36 : this->nOffset = nOffset;
49 36 : }
50 : };
51 :
52 : /************************************************************************/
53 : /* ==================================================================== */
54 : /* VSITarReader */
55 : /* ==================================================================== */
56 : /************************************************************************/
57 :
58 : class VSITarReader : public VSIArchiveReader
59 : {
60 : private:
61 : VSILFILE* fp;
62 : GUIntBig nCurOffset;
63 : GUIntBig nNextFileSize;
64 : CPLString osNextFileName;
65 : GIntBig nModifiedTime;
66 :
67 : public:
68 : VSITarReader(const char* pszTarFileName);
69 : virtual ~VSITarReader();
70 :
71 42 : int IsValid() { return fp != NULL; }
72 :
73 : virtual int GotoFirstFile();
74 : virtual int GotoNextFile();
75 36 : virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSITarEntryFileOffset(nCurOffset); }
76 40 : virtual GUIntBig GetFileSize() { return nNextFileSize; }
77 26 : virtual CPLString GetFileName() { return osNextFileName; }
78 16 : virtual GIntBig GetModifiedTime() { return nModifiedTime; }
79 : virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset);
80 : };
81 :
82 :
83 : /************************************************************************/
84 : /* VSIIsTGZ() */
85 : /************************************************************************/
86 :
87 66 : static int VSIIsTGZ(const char* pszFilename)
88 : {
89 : return (!EQUALN(pszFilename, "/vsigzip/", 9) &&
90 : ((strlen(pszFilename) > 4 &&
91 : EQUALN(pszFilename + strlen(pszFilename) - 4, ".tgz", 4)) ||
92 : (strlen(pszFilename) > 7 &&
93 66 : EQUALN(pszFilename + strlen(pszFilename) - 7, ".tar.gz", 7))));
94 : }
95 :
96 : /************************************************************************/
97 : /* VSITarReader() */
98 : /************************************************************************/
99 :
100 42 : VSITarReader::VSITarReader(const char* pszTarFileName)
101 : {
102 42 : fp = VSIFOpenL(pszTarFileName, "rb");
103 42 : nNextFileSize = 0;
104 42 : nCurOffset = 0;
105 42 : nModifiedTime = 0;
106 42 : }
107 :
108 : /************************************************************************/
109 : /* ~VSITarReader() */
110 : /************************************************************************/
111 :
112 42 : VSITarReader::~VSITarReader()
113 : {
114 42 : if (fp)
115 42 : VSIFCloseL(fp);
116 42 : }
117 :
118 : /************************************************************************/
119 : /* GotoNextFile() */
120 : /************************************************************************/
121 :
122 104 : int VSITarReader::GotoNextFile()
123 : {
124 : char abyHeader[512];
125 104 : if (VSIFReadL(abyHeader, 512, 1, fp) != 1)
126 0 : return FALSE;
127 :
128 832 : if (abyHeader[99] != '\0' ||
129 104 : abyHeader[107] != '\0' ||
130 104 : abyHeader[115] != '\0' ||
131 104 : abyHeader[123] != '\0' ||
132 104 : (abyHeader[135] != '\0' && abyHeader[135] != ' ') ||
133 104 : (abyHeader[147] != '\0' && abyHeader[147] != ' ') ||
134 104 : abyHeader[154] != '\0' ||
135 104 : abyHeader[155] != ' ')
136 18 : return FALSE;
137 :
138 86 : osNextFileName = abyHeader;
139 86 : nNextFileSize = 0;
140 : int i;
141 1032 : for(i=0;i<11;i++)
142 946 : nNextFileSize = nNextFileSize * 8 + (abyHeader[124+i] - '0');
143 :
144 86 : nModifiedTime = 0;
145 1032 : for(i=0;i<11;i++)
146 946 : nModifiedTime = nModifiedTime * 8 + (abyHeader[136+i] - '0');
147 :
148 86 : nCurOffset = VSIFTellL(fp);
149 :
150 86 : GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512;
151 86 : VSIFSeekL(fp, nBytesToSkip, SEEK_CUR);
152 :
153 86 : return TRUE;
154 : }
155 :
156 : /************************************************************************/
157 : /* GotoFirstFile() */
158 : /************************************************************************/
159 :
160 62 : int VSITarReader::GotoFirstFile()
161 : {
162 62 : VSIFSeekL(fp, 0, SEEK_SET);
163 62 : return GotoNextFile();
164 : }
165 :
166 : /************************************************************************/
167 : /* GotoFileOffset() */
168 : /************************************************************************/
169 :
170 16 : int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset)
171 : {
172 16 : VSITarEntryFileOffset* pTarEntryOffset = (VSITarEntryFileOffset*)pOffset;
173 16 : VSIFSeekL(fp, pTarEntryOffset->nOffset - 512, SEEK_SET);
174 16 : return GotoNextFile();
175 : }
176 :
177 : /************************************************************************/
178 : /* ==================================================================== */
179 : /* VSITarFilesystemHandler */
180 : /* ==================================================================== */
181 : /************************************************************************/
182 :
183 : class VSITarFilesystemHandler : public VSIArchiveFilesystemHandler
184 2638 : {
185 : public:
186 210 : virtual const char* GetPrefix() { return "/vsitar"; }
187 : virtual std::vector<CPLString> GetExtensions();
188 : virtual VSIArchiveReader* CreateReader(const char* pszTarFileName);
189 :
190 : virtual VSIVirtualHandle *Open( const char *pszFilename,
191 : const char *pszAccess);
192 : };
193 :
194 :
195 : /************************************************************************/
196 : /* GetExtensions() */
197 : /************************************************************************/
198 :
199 904 : std::vector<CPLString> VSITarFilesystemHandler::GetExtensions()
200 : {
201 904 : std::vector<CPLString> oList;
202 904 : oList.push_back(".tar.gz");
203 904 : oList.push_back(".tar");
204 904 : oList.push_back(".tgz");
205 0 : return oList;
206 : }
207 :
208 : /************************************************************************/
209 : /* CreateReader() */
210 : /************************************************************************/
211 :
212 42 : VSIArchiveReader* VSITarFilesystemHandler::CreateReader(const char* pszTarFileName)
213 : {
214 42 : CPLString osTarInFileName;
215 :
216 42 : if (VSIIsTGZ(pszTarFileName))
217 : {
218 30 : osTarInFileName = "/vsigzip/";
219 30 : osTarInFileName += pszTarFileName;
220 : }
221 : else
222 12 : osTarInFileName = pszTarFileName;
223 :
224 42 : VSITarReader* poReader = new VSITarReader(osTarInFileName);
225 :
226 84 : if (!poReader->IsValid())
227 : {
228 0 : delete poReader;
229 0 : return NULL;
230 : }
231 :
232 42 : if (!poReader->GotoFirstFile())
233 : {
234 0 : delete poReader;
235 0 : return NULL;
236 : }
237 :
238 42 : return poReader;
239 : }
240 :
241 : /************************************************************************/
242 : /* Open() */
243 : /************************************************************************/
244 :
245 46 : VSIVirtualHandle* VSITarFilesystemHandler::Open( const char *pszFilename,
246 : const char *pszAccess)
247 : {
248 : char* tarFilename;
249 46 : CPLString osTarInFileName;
250 :
251 46 : if (strchr(pszAccess, 'w') != NULL ||
252 : strchr(pszAccess, '+') != NULL)
253 : {
254 : CPLError(CE_Failure, CPLE_AppDefined,
255 0 : "Only read-only mode is supported for /vsitar");
256 0 : return NULL;
257 : }
258 :
259 46 : tarFilename = SplitFilename(pszFilename, osTarInFileName, TRUE);
260 46 : if (tarFilename == NULL)
261 16 : return NULL;
262 :
263 30 : VSIArchiveReader* poReader = OpenArchiveFile(tarFilename, osTarInFileName);
264 30 : if (poReader == NULL)
265 : {
266 6 : CPLFree(tarFilename);
267 6 : return NULL;
268 : }
269 :
270 24 : CPLString osSubFileName("/vsisubfile/");
271 24 : VSITarEntryFileOffset* pOffset = (VSITarEntryFileOffset*) poReader->GetFileOffset();
272 24 : osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->nOffset);
273 24 : osSubFileName += "_";
274 24 : osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize());
275 24 : osSubFileName += ",";
276 24 : delete pOffset;
277 :
278 24 : if (VSIIsTGZ(tarFilename))
279 : {
280 16 : osSubFileName += "/vsigzip/";
281 16 : osSubFileName += tarFilename;
282 : }
283 : else
284 8 : osSubFileName += tarFilename;
285 :
286 24 : delete(poReader);
287 :
288 24 : CPLFree(tarFilename);
289 24 : tarFilename = NULL;
290 :
291 24 : return (VSIVirtualHandle* )VSIFOpenL(osSubFileName, "rb");
292 : }
293 :
294 : /************************************************************************/
295 : /* VSIInstallTarFileHandler() */
296 : /************************************************************************/
297 :
298 : /**
299 : * \brief Install /vsitar/ file system handler.
300 : *
301 : * A special file handler is installed that allows reading on-the-fly in TAR
302 : * (regular .tar, or compressed .tar.gz/.tgz) archives.
303 : *
304 : * All portions of the file system underneath the base path "/vsitar/" will be
305 : * handled by this driver.
306 : *
307 : * The syntax to open a file inside a zip file is /vsitar/path/to/the/file.tar/path/inside/the/tar/file
308 : * were path/to/the/file.tar is relative or absolute and path/inside/the/tar/file
309 : * is the relative path to the file inside the archive.
310 : *
311 : * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows),
312 : * so the line looks like /vsitar//home/gdal/...
313 : * For example gdalinfo /vsitar/myarchive.tar/subdir1/file1.tif
314 : *
315 : * Syntaxic sugar : if the tar archive contains only one file located at its root,
316 : * just mentionning "/vsitar/path/to/the/file.tar" will work
317 : *
318 : * VSIStatL() will return the uncompressed size in st_size member and file
319 : * nature- file or directory - in st_mode member.
320 : *
321 : * Directory listing is available through VSIReadDir().
322 : *
323 : * @since GDAL 1.8.0
324 : */
325 :
326 1341 : void VSIInstallTarFileHandler(void)
327 : {
328 1341 : VSIFileManager::InstallHandler( "/vsitar/", new VSITarFilesystemHandler() );
329 1341 : }
|