XRootD
XrdZipCDFH.hh
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // Copyright (c) 2011-2014 by European Organization for Nuclear Research (CERN)
3 // Author: Michal Simon <michal.simon@cern.ch>
4 //------------------------------------------------------------------------------
5 // This file is part of the XRootD software suite.
6 //
7 // XRootD is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // XRootD is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
19 //
20 // In applying this licence, CERN does not waive the privileges and immunities
21 // granted to it by virtue of its status as an Intergovernmental Organization
22 // or submit itself to any jurisdiction.
23 //------------------------------------------------------------------------------
24 
25 #ifndef SRC_XRDZIP_XRDZIPCDFH_HH_
26 #define SRC_XRDZIP_XRDZIPCDFH_HH_
27 
28 #include "XrdZip/XrdZipLFH.hh"
29 #include "XrdZip/XrdZipUtils.hh"
31 
32 #include <string>
33 #include <algorithm>
34 #include <iterator>
35 #include <unordered_map>
36 #include <memory>
37 #include <tuple>
38 
39 #include <sys/types.h>
40 
41 namespace XrdZip
42 {
43  //---------------------------------------------------------------------------
44  // Forward declaration for CDFH
45  //---------------------------------------------------------------------------
46  struct CDFH;
47 
48  //---------------------------------------------------------------------------
49  // Vector of Central Directory records
50  //---------------------------------------------------------------------------
51  typedef std::vector<std::unique_ptr<CDFH>> cdvec_t;
52 
53  //---------------------------------------------------------------------------
54  // Map file name to index of CD record
55  //---------------------------------------------------------------------------
56  typedef std::unordered_map<std::string, size_t> cdmap_t;
57 
58  //---------------------------------------------------------------------------
59  // Map of Central Directory records
60  //---------------------------------------------------------------------------
61  typedef std::unordered_map<std::string, std::unique_ptr<CDFH>> cdrecs_t;
62 
63  //---------------------------------------------------------------------------
64  // A data structure representing the Central Directory File header record
65  //---------------------------------------------------------------------------
66  struct CDFH
67  {
68  //-------------------------------------------------------------------------
69  // Parse central directory
70  // @param buffer : buffer containing the CD records
71  // @param bufferSize : size of the buffer
72  // @param nbCdRecords : nb of CD records
73  // @return : vector of CD records / file name to index mapping
74  //-------------------------------------------------------------------------
75  inline static std::tuple<cdvec_t, cdmap_t> Parse( const char *buffer, uint32_t bufferSize, uint16_t nbCdRecords )
76  {
77  uint32_t offset = 0;
78  cdvec_t cdvec;
79  cdmap_t cdmap;
80  cdvec.reserve( nbCdRecords );
81 
82  for( size_t i = 0; i < nbCdRecords; ++i )
83  {
84  if( bufferSize < cdfhBaseSize ) break;
85  // check the signature
86  uint32_t signature = to<uint32_t>( buffer + offset );
87  if( signature != cdfhSign ) throw bad_data();
88  // parse the record
89  std::unique_ptr<CDFH> cdfh( new CDFH( buffer + offset, bufferSize ) );
90  offset += cdfh->cdfhSize;
91  bufferSize -= cdfh->cdfhSize;
92  cdmap[cdfh->filename] = i;
93  cdvec.push_back( std::move( cdfh ) );
94  }
95 
96  return std::make_tuple( std::move( cdvec ), std::move( cdmap ) );
97  }
98 
99  //-------------------------------------------------------------------------
100  // Parse central directory
101  // @param buffer : buffer containing the CD records
102  // @param bufferSize : size of the buffer
103  // @return : vector of CD records / file name to index mapping
104  //-------------------------------------------------------------------------
105  inline static std::tuple<cdvec_t, cdmap_t> Parse( const char *&buffer, uint32_t bufferSize )
106  {
107  cdvec_t cdvec;
108  cdmap_t cdmap;
109  size_t i = 0;
110  while( bufferSize > 0 )
111  {
112  if( bufferSize < sizeof( uint32_t ) ) throw bad_data();
113  // check the signature
114  uint32_t signature = to<uint32_t>( buffer );
115  if( signature != cdfhSign )
116  return std::make_tuple( std::move( cdvec ), std::move( cdmap ) );
117  // parse the record
118  std::unique_ptr<CDFH> cdfh( new CDFH( buffer ) );
119  if( bufferSize < cdfh->cdfhSize ) throw bad_data();
120  buffer += cdfh->cdfhSize;
121  bufferSize -= cdfh->cdfhSize;
122  cdmap[cdfh->filename] = i++;
123  cdvec.push_back( std::move( cdfh ) );
124  }
125 
126  return std::make_tuple( std::move( cdvec ), std::move( cdmap ) );
127  }
128 
129  //---------------------------------------------------------------------------
130  // Calculate size of the Central Directory
131  //---------------------------------------------------------------------------
132  inline static size_t CalcSize( const cdvec_t &cdvec, uint32_t orgcdsz, uint32_t orgcdcnt )
133  {
134  size_t size = 0;
135  auto itr = cdvec.begin() + orgcdcnt;
136  for( ; itr != cdvec.end() ; ++itr )
137  {
138  CDFH *cdfh = itr->get();
139  size += cdfh->cdfhSize;
140  }
141  return size + orgcdsz;
142  }
143 
144  inline static void Serialize( uint32_t orgcdcnt,
145  const buffer_t &orgcdbuf,
146  const cdvec_t &cdvec,
147  buffer_t &buffer )
148  {
149  std::copy( orgcdbuf.begin(), orgcdbuf.end(), std::back_inserter( buffer ) );
150  auto itr = cdvec.begin() + orgcdcnt;
151  for( ; itr != cdvec.end() ; ++itr )
152  {
153  CDFH *cdfh = itr->get();
154  cdfh->Serialize( buffer );
155  }
156  }
157 
158  //-------------------------------------------------------------------------
159  // Constructor from Local File Header
160  //-------------------------------------------------------------------------
161  CDFH( LFH *lfh, mode_t mode, uint64_t lfhOffset ):
162  zipVersion( ( 3 << 8 ) | 63 ),
165  timestmp( lfh->timestmp ),
166  ZCRC32( lfh->ZCRC32 ),
170  commentLength( 0 ),
171  nbDisk( 0 ),
172  internAttr( 0 ),
173  externAttr( mode << 16 ),
174  filename( lfh->filename ),
175  extra( new Extra( lfh->extra.get(), lfhOffset ) )
176  {
177  if ( lfhOffset >= ovrflw<uint32_t>::value )
179  else
180  offset = lfhOffset;
181 
182  extraLength = extra->totalSize;
183 
184  if ( extraLength == 0 )
185  minZipVersion = 10;
186  else
187  minZipVersion = 45;
188 
190  }
191 
192  //-------------------------------------------------------------------------
193  // Constructor from buffer
194  //-------------------------------------------------------------------------
195  CDFH( const char *buffer, const uint32_t maxSize = 0 )
196  {
197  zipVersion = to<uint16_t>(buffer + 4);
198  minZipVersion = to<uint16_t>(buffer + 6);
199  generalBitFlag = to<uint16_t>(buffer + 8);
200  compressionMethod = to<uint16_t>(buffer + 10);
201  timestmp.time = to<uint16_t>(buffer + 12);
202  timestmp.date = to<uint16_t>(buffer + 14);
203  ZCRC32 = to<uint32_t>(buffer + 16);
204  compressedSize = to<uint32_t>(buffer + 20);
205  uncompressedSize = to<uint32_t>(buffer + 24);
206  filenameLength = to<uint16_t>(buffer + 28);
207  extraLength = to<uint16_t>(buffer + 30);
208  commentLength = to<uint16_t>(buffer + 32);
209  nbDisk = to<uint16_t>(buffer + 34);
210  internAttr = to<uint16_t>(buffer + 36);
211  externAttr = to<uint32_t>(buffer + 38);
212  offset = to<uint32_t>(buffer + 42);
213  if(maxSize > 0 && (uint32_t)(cdfhBaseSize+filenameLength + extraLength + commentLength) > maxSize){
214  throw bad_data();
215  }
216  filename.assign( buffer + 46, filenameLength );
217 
218  // now parse the 'extra' (may contain the zip64 extension to CDFH)
219  ParseExtra( buffer + 46 + filenameLength, extraLength );
220 
222  }
223 
224  //-------------------------------------------------------------------------
225  // Choose the right offset value from the CDFH record
226  //-------------------------------------------------------------------------
227  inline static uint64_t GetOffset( const CDFH &cdfh )
228  {
229  if( cdfh.offset != ovrflw<uint32_t>::value )
230  return cdfh.offset;
231  return cdfh.extra->offset;
232  }
233 
234  //-------------------------------------------------------------------------
235  // Parse the extensible data fields
236  //-------------------------------------------------------------------------
237  void ParseExtra( const char *buffer, uint16_t length)
238  {
239  uint8_t ovrflws = Extra::NONE;
240  uint16_t exsize = 0;
241 
242  // check if compressed size is overflown
244  {
245  ovrflws |= Extra::CPMSIZE;
246  exsize += sizeof( uint64_t );
247  }
248 
249  // check if original size is overflown
251  {
252  ovrflws |= Extra::UCMPSIZE;
253  exsize += sizeof( uint64_t );
254  }
255 
256  // check if offset is overflown
258  {
259  ovrflws |= Extra::OFFSET;
260  exsize += sizeof( uint64_t );
261  }
262 
263  // check if number of disks is overflown
265  {
266  ovrflws |= Extra::NBDISK;
267  exsize += sizeof( uint32_t );
268  }
269 
270  // if the expected size of ZIP64 extension is 0 we
271  // can skip parsing of 'extra'
272  if( exsize == 0 ) return;
273 
274  // Parse the extra part
275  buffer = Extra::Find( buffer, length );
276  if( buffer )
277  {
278  extra.reset( new Extra() );
279  extra->FromBuffer( buffer, exsize, ovrflws );
280  }
281  }
282 
283  //-------------------------------------------------------------------------
285  //-------------------------------------------------------------------------
286  void Serialize( buffer_t &buffer )
287  {
288  copy_bytes( cdfhSign, buffer );
289  copy_bytes( zipVersion, buffer );
290  copy_bytes( minZipVersion, buffer );
291  copy_bytes( generalBitFlag, buffer );
292  copy_bytes( compressionMethod, buffer );
293  copy_bytes( timestmp.time, buffer );
294  copy_bytes( timestmp.date, buffer );
295  copy_bytes( ZCRC32, buffer );
296  copy_bytes( compressedSize, buffer );
297  copy_bytes( uncompressedSize, buffer );
298  copy_bytes( filenameLength, buffer );
299  copy_bytes( extraLength, buffer );
300  copy_bytes( commentLength, buffer );
301  copy_bytes( nbDisk, buffer );
302  copy_bytes( internAttr, buffer );
303  copy_bytes( externAttr, buffer );
304  copy_bytes( offset, buffer );
305  std::copy( filename.begin(), filename.end(), std::back_inserter( buffer ) );
306  if( extra )
307  extra->Serialize( buffer );
308 
309  if ( commentLength > 0 )
310  std::copy( comment.begin(), comment.end(), std::back_inserter( buffer ) );
311  }
312 
313  //-------------------------------------------------------------------------
315  //-------------------------------------------------------------------------
316  inline bool IsZIP64() const
317  {
318  return extra.get();
319  }
320 
321  //-------------------------------------------------------------------------
323  //-------------------------------------------------------------------------
325  {
327  }
328 
329  uint16_t zipVersion; // ZIP version
330  uint16_t minZipVersion; //< minumum ZIP version
331  uint16_t generalBitFlag; //< flags
332  uint16_t compressionMethod; //< compression method
333  dos_timestmp timestmp; //< DOS timestamp
334  uint32_t ZCRC32; //< CRC32
335  uint32_t compressedSize; //< compressed size
336  uint32_t uncompressedSize; //< uncompressed size
337  uint16_t filenameLength; //< filename length
338  uint16_t extraLength; //< size of the ZIP64 extra field
339  uint16_t commentLength; //< comment length
340  uint16_t nbDisk; //< number of disks
341  uint16_t internAttr; //< internal attributes
342  uint32_t externAttr; //< external attributes
343  uint32_t offset; //< offset
344  std::string filename; //< file name
345  std::unique_ptr<Extra> extra; //< ZIP64 extra field
346  std::string comment; //< user comment
347  uint16_t cdfhSize; // size of the record
348 
349  //-------------------------------------------------------------------------
350  // the Central Directory File Header signature
351  //-------------------------------------------------------------------------
352  static const uint32_t cdfhSign = 0x02014b50;
353  static const uint16_t cdfhBaseSize = 46;
354  };
355 }
356 
357 #endif /* SRC_XRDZIP_XRDZIPCDFH_HH_ */
std::unordered_map< std::string, std::unique_ptr< CDFH > > cdrecs_t
Definition: XrdZipCDFH.hh:61
std::vector< std::unique_ptr< CDFH > > cdvec_t
Definition: XrdZipCDFH.hh:46
std::vector< char > buffer_t
Definition: XrdZipUtils.hh:56
static void copy_bytes(const INT value, buffer_t &buffer)
Definition: XrdZipUtils.hh:62
std::unordered_map< std::string, size_t > cdmap_t
Definition: XrdZipCDFH.hh:56
static std::tuple< cdvec_t, cdmap_t > Parse(const char *&buffer, uint32_t bufferSize)
Definition: XrdZipCDFH.hh:105
uint32_t uncompressedSize
Definition: XrdZipCDFH.hh:336
uint32_t offset
Definition: XrdZipCDFH.hh:343
uint16_t cdfhSize
Definition: XrdZipCDFH.hh:347
uint16_t nbDisk
Definition: XrdZipCDFH.hh:340
static uint64_t GetOffset(const CDFH &cdfh)
Definition: XrdZipCDFH.hh:227
std::unique_ptr< Extra > extra
Definition: XrdZipCDFH.hh:345
uint16_t internAttr
Definition: XrdZipCDFH.hh:341
std::string filename
Definition: XrdZipCDFH.hh:344
CDFH(LFH *lfh, mode_t mode, uint64_t lfhOffset)
Definition: XrdZipCDFH.hh:161
uint32_t externAttr
Definition: XrdZipCDFH.hh:342
uint16_t zipVersion
Definition: XrdZipCDFH.hh:329
static const uint16_t cdfhBaseSize
Definition: XrdZipCDFH.hh:353
uint16_t extraLength
Definition: XrdZipCDFH.hh:338
static std::tuple< cdvec_t, cdmap_t > Parse(const char *buffer, uint32_t bufferSize, uint16_t nbCdRecords)
Definition: XrdZipCDFH.hh:75
uint16_t compressionMethod
Definition: XrdZipCDFH.hh:332
bool HasDataDescriptor()
Definition: XrdZipCDFH.hh:324
uint16_t generalBitFlag
Definition: XrdZipCDFH.hh:331
void Serialize(buffer_t &buffer)
Serialize the object into a buffer.
Definition: XrdZipCDFH.hh:286
uint32_t ZCRC32
Definition: XrdZipCDFH.hh:334
uint16_t commentLength
Definition: XrdZipCDFH.hh:339
uint16_t minZipVersion
Definition: XrdZipCDFH.hh:330
static size_t CalcSize(const cdvec_t &cdvec, uint32_t orgcdsz, uint32_t orgcdcnt)
Definition: XrdZipCDFH.hh:132
static const uint32_t cdfhSign
Definition: XrdZipCDFH.hh:352
static void Serialize(uint32_t orgcdcnt, const buffer_t &orgcdbuf, const cdvec_t &cdvec, buffer_t &buffer)
Definition: XrdZipCDFH.hh:144
void ParseExtra(const char *buffer, uint16_t length)
Definition: XrdZipCDFH.hh:237
uint32_t compressedSize
Definition: XrdZipCDFH.hh:335
uint16_t filenameLength
Definition: XrdZipCDFH.hh:337
CDFH(const char *buffer, const uint32_t maxSize=0)
Definition: XrdZipCDFH.hh:195
bool IsZIP64() const
Definition: XrdZipCDFH.hh:316
dos_timestmp timestmp
Definition: XrdZipCDFH.hh:333
std::string comment
Definition: XrdZipCDFH.hh:346
static const uint16_t flag
static const char * Find(const char *buffer, uint16_t length)
Definition: XrdZipExtra.hh:101
A data structure representing ZIP Local File Header.
Definition: XrdZipLFH.hh:42