1 : /******************************************************************************
2 : * $Id: cpl_vsil_tar.cpp 20028 2010-07-11 18:20:55Z 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 20028 2010-07-11 18:20:55Z rouault $");
33 :
34 :
35 : /************************************************************************/
36 : /* ==================================================================== */
37 : /* VSITarEntryFileOffset */
38 : /* ==================================================================== */
39 : /************************************************************************/
40 :
41 : class VSITarEntryFileOffset : public VSIArchiveEntryFileOffset
42 13 : {
43 : public:
44 : GUIntBig nOffset;
45 :
46 19 : VSITarEntryFileOffset(GUIntBig nOffset)
47 19 : {
48 19 : this->nOffset = nOffset;
49 19 : }
50 : };
51 :
52 : /************************************************************************/
53 : /* ==================================================================== */
54 : /* VSITarReader */
55 : /* ==================================================================== */
56 : /************************************************************************/
57 :
58 : class VSITarReader : public VSIArchiveReader
59 : {
60 : private:
61 : FILE* 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 30 : int IsValid() { return fp != NULL; }
72 :
73 : virtual int GotoFirstFile();
74 : virtual int GotoNextFile();
75 19 : virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSITarEntryFileOffset(nCurOffset); }
76 21 : virtual GUIntBig GetFileSize() { return nNextFileSize; }
77 13 : virtual CPLString GetFileName() { return osNextFileName; }
78 8 : virtual GIntBig GetModifiedTime() { return nModifiedTime; }
79 : virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset);
80 : };
81 :
82 :
83 : /************************************************************************/
84 : /* VSIIsTGZ() */
85 : /************************************************************************/
86 :
87 43 : 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 43 : EQUALN(pszFilename + strlen(pszFilename) - 7, ".tar.gz", 7))));
94 : }
95 :
96 : /************************************************************************/
97 : /* VSITarReader() */
98 : /************************************************************************/
99 :
100 30 : VSITarReader::VSITarReader(const char* pszTarFileName)
101 : {
102 30 : fp = VSIFOpenL(pszTarFileName, "rb");
103 30 : nNextFileSize = 0;
104 30 : nCurOffset = 0;
105 30 : nModifiedTime = 0;
106 30 : }
107 :
108 : /************************************************************************/
109 : /* ~VSITarReader() */
110 : /************************************************************************/
111 :
112 30 : VSITarReader::~VSITarReader()
113 : {
114 30 : if (fp)
115 30 : VSIFCloseL(fp);
116 30 : }
117 :
118 : /************************************************************************/
119 : /* GotoNextFile() */
120 : /************************************************************************/
121 :
122 62 : int VSITarReader::GotoNextFile()
123 : {
124 : char abyHeader[512];
125 62 : if (VSIFReadL(abyHeader, 512, 1, fp) != 1)
126 0 : return FALSE;
127 :
128 62 : if (abyHeader[99] != '\0' ||
129 : abyHeader[107] != '\0' ||
130 : abyHeader[115] != '\0' ||
131 : abyHeader[123] != '\0' ||
132 : abyHeader[135] != '\0' ||
133 : abyHeader[147] != '\0' ||
134 : abyHeader[154] != '\0' ||
135 : abyHeader[155] != ' ')
136 9 : return FALSE;
137 :
138 53 : osNextFileName = abyHeader;
139 53 : nNextFileSize = 0;
140 : int i;
141 636 : for(i=0;i<11;i++)
142 583 : nNextFileSize = nNextFileSize * 8 + (abyHeader[124+i] - '0');
143 :
144 53 : nModifiedTime = 0;
145 636 : for(i=0;i<11;i++)
146 583 : nModifiedTime = nModifiedTime * 8 + (abyHeader[136+i] - '0');
147 :
148 53 : nCurOffset = VSIFTellL(fp);
149 :
150 53 : GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512;
151 53 : VSIFSeekL(fp, nBytesToSkip, SEEK_CUR);
152 :
153 53 : return TRUE;
154 : }
155 :
156 : /************************************************************************/
157 : /* GotoFirstFile() */
158 : /************************************************************************/
159 :
160 40 : int VSITarReader::GotoFirstFile()
161 : {
162 40 : VSIFSeekL(fp, 0, SEEK_SET);
163 40 : return GotoNextFile();
164 : }
165 :
166 : /************************************************************************/
167 : /* GotoFileOffset() */
168 : /************************************************************************/
169 :
170 9 : int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset)
171 : {
172 9 : VSITarEntryFileOffset* pTarEntryOffset = (VSITarEntryFileOffset*)pOffset;
173 9 : VSIFSeekL(fp, pTarEntryOffset->nOffset - 512, SEEK_SET);
174 9 : return GotoNextFile();
175 : }
176 :
177 : /************************************************************************/
178 : /* ==================================================================== */
179 : /* VSITarFilesystemHandler */
180 : /* ==================================================================== */
181 : /************************************************************************/
182 :
183 : class VSITarFilesystemHandler : public VSIArchiveFilesystemHandler
184 879 : {
185 : public:
186 120 : 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 816 : std::vector<CPLString> VSITarFilesystemHandler::GetExtensions()
200 : {
201 816 : std::vector<CPLString> oList;
202 1632 : oList.push_back(".tar.gz");
203 816 : oList.push_back(".tar");
204 816 : oList.push_back(".tgz");
205 0 : return oList;
206 : }
207 :
208 : /************************************************************************/
209 : /* CreateReader() */
210 : /************************************************************************/
211 :
212 30 : VSIArchiveReader* VSITarFilesystemHandler::CreateReader(const char* pszTarFileName)
213 : {
214 30 : CPLString osTarInFileName;
215 :
216 30 : if (VSIIsTGZ(pszTarFileName))
217 : {
218 20 : osTarInFileName = "/vsigzip/";
219 20 : osTarInFileName += pszTarFileName;
220 : }
221 : else
222 10 : osTarInFileName = pszTarFileName;
223 :
224 30 : VSITarReader* poReader = new VSITarReader(osTarInFileName);
225 :
226 60 : if (!poReader->IsValid())
227 : {
228 0 : delete poReader;
229 0 : return NULL;
230 : }
231 :
232 30 : if (!poReader->GotoFirstFile())
233 : {
234 0 : delete poReader;
235 0 : return NULL;
236 : }
237 :
238 30 : return poReader;
239 : }
240 :
241 : /************************************************************************/
242 : /* Open() */
243 : /************************************************************************/
244 :
245 : VSIVirtualHandle* VSITarFilesystemHandler::Open( const char *pszFilename,
246 32 : const char *pszAccess)
247 : {
248 : char* tarFilename;
249 32 : CPLString osTarInFileName;
250 :
251 32 : 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 32 : tarFilename = SplitFilename(pszFilename, osTarInFileName);
260 32 : if (tarFilename == NULL)
261 8 : return NULL;
262 :
263 24 : VSIArchiveReader* poReader = OpenArchiveFile(tarFilename, osTarInFileName);
264 24 : if (poReader == NULL)
265 : {
266 11 : CPLFree(tarFilename);
267 11 : return NULL;
268 : }
269 :
270 13 : CPLString osSubFileName("/vsisubfile/");
271 13 : VSITarEntryFileOffset* pOffset = (VSITarEntryFileOffset*) poReader->GetFileOffset();
272 13 : osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->nOffset);
273 13 : osSubFileName += "_";
274 13 : osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize());
275 13 : osSubFileName += ",";
276 13 : delete pOffset;
277 :
278 13 : if (VSIIsTGZ(tarFilename))
279 : {
280 9 : osSubFileName += "/vsigzip/";
281 9 : osSubFileName += tarFilename;
282 : }
283 : else
284 4 : osSubFileName += tarFilename;
285 :
286 13 : delete(poReader);
287 :
288 13 : CPLFree(tarFilename);
289 13 : tarFilename = NULL;
290 :
291 13 : return (VSIVirtualHandle* )VSIFOpenL(osSubFileName, "rb");
292 : }
293 :
294 : /************************************************************************/
295 : /* VSIInstallTarFileHandler() */
296 : /************************************************************************/
297 :
298 :
299 : /**
300 : * \brief Install TAR file system handler.
301 : *
302 : * A special file handler is installed that allows reading on-the-fly in TAR (.tar, .tar.gz/.tgz) archives.
303 : * All portions of the file system underneath the base
304 : * path "/vsitar/" will be handled by this driver.
305 : *
306 : */
307 :
308 447 : void VSIInstallTarFileHandler(void)
309 : {
310 447 : VSIFileManager::InstallHandler( "/vsitar/", new VSITarFilesystemHandler() );
311 447 : }
|