Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2000-2007 Thomas Sondergaard
5 : Copyright (c) 2015-2022 Made to Order Software Corp. All Rights Reserved
6 :
7 : This library is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public
9 : License as published by the Free Software Foundation; either
10 : version 2.1 of the License, or (at your option) any later version.
11 :
12 : This library 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 GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this library; if not, write to the Free Software
19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : /** \file
23 : * \brief Declare zipios::ZipEndOfCentralDirectory which handles entries found
24 : * in a Zip archive directory.
25 : *
26 : * This header file contains the zipios::ZipLocalEntry, which is used
27 : * to handle entries found in a Zip archive.
28 : */
29 :
30 : #include "zipendofcentraldirectory.hpp"
31 :
32 : #include "zipios/zipiosexceptions.hpp"
33 :
34 :
35 : namespace zipios
36 : {
37 :
38 :
39 : /** \class ZipEndOfCentralDirectory
40 : * \brief Marker at the end of a Zip archive file.
41 : *
42 : * This class is used to read and write the end of the Central Directory
43 : * structure. In most cases, this structure is stored at the end of the
44 : * zip archive file, and contains some global information about the file,
45 : * including the position of the start of the Central Directory.
46 : */
47 :
48 :
49 : /** \brief Private definitions of the ZipEndOfCentralDirectory class.
50 : *
51 : * This name space includes definitions exclusively used by the
52 : * ZipEndOfCentralDirectory class.
53 : */
54 : namespace
55 : {
56 :
57 :
58 : /** \brief Signature of the ZipEndOfCentralDirectory structure block.
59 : *
60 : * This variable is used to define the signature of the
61 : * ZipEndOfCentralDirectory structure on disk. It is used to
62 : * create such a block or to detect that such a block exists.
63 : *
64 : * The four byte signature represents the following value:
65 : *
66 : * "PK 5.6" -- End of Central Directory
67 : */
68 : uint32_t const g_signature = 0x06054b50;
69 :
70 :
71 : } // no name namespace
72 :
73 :
74 : /** \brief Initialize an ZipEndOfCentralDirectory object.
75 : *
76 : * This function initializes an ZipEndOfCentralDirectory object. By default,
77 : * all the numbers are set to zero and the global Zip file comment is
78 : * set to the empty string.
79 : *
80 : * \param[in] zip_comment The global comment of a Zip archive.
81 : */
82 625 : ZipEndOfCentralDirectory::ZipEndOfCentralDirectory(std::string const & zip_comment)
83 625 : : m_zip_comment(zip_comment)
84 : {
85 625 : }
86 :
87 :
88 : /** \brief Retrieve the size of the Central Directory in bytes.
89 : *
90 : * This function returns the size of the Central Directory
91 : * structure in the file. This size varies because each entry
92 : * includes data that change in size (i.e. filename, comment,
93 : * extra data.)
94 : *
95 : * \return The size, in bytes, of the Central Directory.
96 : *
97 : * \sa setCentralDirectorySize()
98 : */
99 315 : size_t ZipEndOfCentralDirectory::getCentralDirectorySize() const
100 : {
101 315 : return m_central_directory_size;
102 : }
103 :
104 :
105 : /** \brief Retrieve the number of entries.
106 : *
107 : * This function returns the number of entries that will be found
108 : * in the Central Directory.
109 : *
110 : * Since Zipios has no support for split Zip archive files (i.e. one
111 : * large archive written on multiple disks), the total number of entries,
112 : * or the number of entries in this archive is always exactly the same.
113 : *
114 : * \return The total number of entries archived in this Zip file.
115 : *
116 : * \sa setCount()
117 : */
118 650 : size_t ZipEndOfCentralDirectory::getCount() const
119 : {
120 650 : return m_central_directory_entries;
121 : }
122 :
123 :
124 : /** \brief Retrieve the offset of the Central Directory.
125 : *
126 : * This function is expected to be called after a call to read().
127 : * It includes the offset of the central directory, which in most
128 : * cases appears before the ZipEndOfCentralDirectory block.
129 : *
130 : * \warning
131 : * There is getOffsetFromEnd() which returns the offset of the
132 : * ZipEndOfCentralDirectory itself and not the Central Directory.
133 : *
134 : * \return The offset in the Zip archive of the Central Directory.
135 : *
136 : * \sa getOffsetFromEnd()
137 : * \sa setOffset()
138 : */
139 640 : offset_t ZipEndOfCentralDirectory::getOffset() const
140 : {
141 640 : return m_central_directory_offset;
142 : }
143 :
144 :
145 : /** \brief Define the size of the central directory.
146 : *
147 : * When creating a Zip archive, it is necessary to call this function
148 : * to define the size of the Central Directory block. This size
149 : * cannot be inferred or calculated without wasting a lot of time
150 : * re-reading the Central Directory, hence the function to avoid
151 : * doing such.
152 : *
153 : * \param[in] size The size of the Central Directory.
154 : *
155 : * \sa getCentralDirectorySize()
156 : */
157 255 : void ZipEndOfCentralDirectory::setCentralDirectorySize(size_t size)
158 : {
159 255 : m_central_directory_size = size;
160 255 : }
161 :
162 :
163 : /** \brief Set the number of entries.
164 : *
165 : * This function is used to define the number of entries one will find
166 : * in the central directory.
167 : *
168 : * \note
169 : * The maximum number of entries is 65535. (until we add support for
170 : * 64 bit Zip archives.)
171 : *
172 : * \param[in] count The number of entries in the Central Directory.
173 : *
174 : * \sa getCount()
175 : */
176 257 : void ZipEndOfCentralDirectory::setCount(size_t count)
177 : {
178 257 : m_central_directory_entries = count;
179 257 : }
180 :
181 :
182 : /** \brief Offset of the Central Directory.
183 : *
184 : * This function defines the offset at which the Central Directory
185 : * starts. Before writing the Central Directory, we expect the user
186 : * to call tell() and save the value using this function. This is
187 : * important when creating a Zip archive.
188 : *
189 : * \note
190 : * Only the offset of the Central Directory can be changed by
191 : * this function.
192 : *
193 : * \param[in] start_offset The start offset of the Central Directory.
194 : *
195 : * \sa getOffset();
196 : */
197 257 : void ZipEndOfCentralDirectory::setOffset(offset_t start_offset)
198 : {
199 257 : m_central_directory_offset = start_offset;
200 257 : }
201 :
202 :
203 : /** \brief Attempt to read an ZipEndOfCentralDirectory structure.
204 : *
205 : * This function tries to read an ZipEndOfCentralDirectory structure from the
206 : * specified buffer. This function expects a BackBuffer, which is used
207 : * because that is generally the fastest way to read the data (instead of
208 : * scanning the entire file).
209 : *
210 : * \note
211 : * If a read from the buffer fails, then an exception is raised. Since
212 : * we are reading from a buffer, it should not happen except if the
213 : * ZipEndOfCentralDirectory indicates that there is a comment and the comment
214 : * is not there or some characters are missing.
215 : *
216 : * \exception FileCollectionException
217 : * This exception is raised if the number of entries is not equal to
218 : * the total number of entries, as expected.
219 : *
220 : * \param[in] buf The buffer with the file data.
221 : * \param[in] pos The position at which we are expected to check.
222 : *
223 : * \return true if the ZipEndOfCentralDirectory was found, false otherwise.
224 : */
225 9145 : bool ZipEndOfCentralDirectory::read(::zipios::buffer_t const & buf, size_t pos)
226 : {
227 : // the number of bytes we are going to read in the buffer
228 : // (including the signature)
229 9145 : ssize_t const HEADER_SIZE(static_cast<ssize_t>(sizeof(uint32_t) * 3 + sizeof(uint16_t) * 5));
230 :
231 : // enough data in the buffer?
232 : //
233 : // Note: this quick check assumes a 0 length comment which is possible;
234 : // if there is a comment and we find the signature too early, then
235 : // it will throw
236 : //
237 9145 : if(static_cast<ssize_t>(buf.size() - pos) < HEADER_SIZE)
238 : {
239 7518 : return false;
240 : }
241 :
242 : // first read and check the signature
243 : uint32_t signature;
244 1627 : zipRead(buf, pos, signature); // 32
245 1627 : if(signature != g_signature)
246 : {
247 1282 : return false;
248 : }
249 :
250 : // next we read the other parameters
251 : uint16_t disk_number;
252 : uint16_t central_directory_entries;
253 : uint16_t central_directory_total_entries;
254 : uint32_t central_directory_size;
255 : uint32_t central_directory_offset;
256 : uint16_t comment_len;
257 :
258 345 : zipRead(buf, pos, disk_number); // 16
259 345 : zipRead(buf, pos, disk_number); // 16
260 345 : zipRead(buf, pos, central_directory_entries); // 16
261 345 : zipRead(buf, pos, central_directory_total_entries); // 16
262 345 : zipRead(buf, pos, central_directory_size); // 32
263 345 : zipRead(buf, pos, central_directory_offset); // 32
264 345 : zipRead(buf, pos, comment_len); // 16
265 345 : zipRead(buf, pos, m_zip_comment, comment_len); // string
266 :
267 : // note that if disk_number is defined, then these following two
268 : // numbers should differ too
269 335 : if(central_directory_entries != central_directory_total_entries)
270 : {
271 10 : throw FileCollectionException("ZipEndOfCentralDirectory with a number of entries and total entries that differ is not supported, spanned zip files are not supported");
272 : }
273 :
274 325 : m_central_directory_entries = central_directory_entries;
275 325 : m_central_directory_size = central_directory_size;
276 325 : m_central_directory_offset = central_directory_offset;
277 :
278 325 : return true;
279 : }
280 :
281 :
282 : /** \brief Write the ZipEndOfCentralDirectory structure to a stream.
283 : *
284 : * This function writes the currently defined end of central
285 : * directory to disk. This entry is expected to be written at
286 : * the very end of a Zip archive file.
287 : *
288 : * \note
289 : * If the output pointer is not valid, the function will throw
290 : * via the various zipWrite() it uses.
291 : *
292 : * \note
293 : * The function does not change the output pointer of the stream
294 : * before writing to it.
295 : *
296 : * \exception FileCollectionException
297 : * This function throws this exception if the data cannot be saved. In
298 : * general this means there are too many entries, the size is too large
299 : * or the comment is more than 64Kb (some of which will be resolved with
300 : * Zip64 support.)
301 : *
302 : * \param[in] os The output stream where the data is to be saved.
303 : */
304 255 : void ZipEndOfCentralDirectory::write(std::ostream & os)
305 : {
306 : /** \todo
307 : * Add support for 64 bit Zip archive. This would allow for pretty
308 : * much all the following conditions to be dropped out.
309 : */
310 255 : if(m_zip_comment.length() > 65535)
311 : {
312 1 : throw InvalidStateException("the Zip archive comment is too large");
313 : }
314 254 : if(m_central_directory_entries > 65535)
315 : {
316 1 : throw InvalidStateException("the number of entries in the Zip archive is too large");
317 : }
318 : // Solaris defines _ILP32 for 32 bit platforms
319 : #if INTPTR_MAX != INT32_MAX
320 253 : if(m_central_directory_size >= 0x100000000UL
321 253 : || m_central_directory_offset >= 0x100000000L)
322 : {
323 : throw FileCollectionException("the Zip archive size or offset are too large"); // LCOV_EXCL_LINE
324 : }
325 : #endif
326 :
327 253 : uint16_t const disk_number(0);
328 253 : uint16_t const central_directory_entries(m_central_directory_entries);
329 253 : uint32_t const central_directory_size(m_central_directory_size);
330 253 : uint32_t const central_directory_offset(m_central_directory_offset);
331 253 : uint16_t const comment_len(m_zip_comment.length());
332 :
333 : // the total number of entries, across all disks is the same in our
334 : // case so we use one number for both fields
335 :
336 253 : zipWrite(os, g_signature); // 32
337 253 : zipWrite(os, disk_number); // 16
338 253 : zipWrite(os, disk_number); // 16
339 253 : zipWrite(os, central_directory_entries); // 16
340 253 : zipWrite(os, central_directory_entries); // 16
341 253 : zipWrite(os, central_directory_size); // 32
342 253 : zipWrite(os, central_directory_offset); // 32
343 253 : zipWrite(os, comment_len); // 16
344 253 : zipWrite(os, m_zip_comment); // string
345 253 : }
346 :
347 :
348 : } // zipios namespace
349 :
350 : // Local Variables:
351 : // mode: cpp
352 : // indent-tabs-mode: nil
353 : // c-basic-offset: 4
354 : // tab-width: 4
355 : // End:
356 :
357 : // vim: ts=4 sw=4 et
|