/src/CMake/Source/cmArchiveWrite.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmArchiveWrite.h" |
4 | | |
5 | | #include <cstdlib> |
6 | | #include <ctime> |
7 | | #include <iostream> |
8 | | #include <limits> |
9 | | #include <sstream> |
10 | | #include <string> |
11 | | #include <thread> |
12 | | |
13 | | #include <cm/algorithm> |
14 | | #include <cm/string_view> |
15 | | |
16 | | #include <cm3p/archive.h> |
17 | | #include <cm3p/archive_entry.h> |
18 | | |
19 | | #include "cmsys/Directory.hxx" |
20 | | #ifdef _WIN32 |
21 | | # include "cmsys/Encoding.hxx" |
22 | | #endif |
23 | | #include "cmsys/FStream.hxx" |
24 | | |
25 | | #include "cm_parse_date.h" |
26 | | |
27 | | #include "cmStringAlgorithms.h" |
28 | | #include "cmSystemTools.h" |
29 | | |
30 | | #ifndef __LA_SSIZE_T |
31 | | # define __LA_SSIZE_T la_ssize_t |
32 | | #endif |
33 | | |
34 | | static std::string cm_archive_error_string(struct archive* a) |
35 | 0 | { |
36 | 0 | char const* e = archive_error_string(a); |
37 | 0 | return e ? e : "unknown error"; |
38 | 0 | } |
39 | | |
40 | | // Set path to be written to the archive. |
41 | | static void cm_archive_entry_copy_pathname(struct archive_entry* e, |
42 | | char const* dest) |
43 | 0 | { |
44 | | #ifdef _WIN32 |
45 | | // libarchive converts our UTF-8 encoding to the archive's encoding. |
46 | | archive_entry_update_pathname_utf8(e, dest); |
47 | | #else |
48 | | // libarchive converts our locale's encoding to the archive's encoding. |
49 | 0 | archive_entry_copy_pathname(e, dest); |
50 | 0 | #endif |
51 | 0 | } |
52 | | |
53 | | // Set path used for filesystem access. |
54 | | static void cm_archive_entry_copy_sourcepath(struct archive_entry* e, |
55 | | std::string const& file) |
56 | 0 | { |
57 | | #ifdef _WIN32 |
58 | | archive_entry_copy_sourcepath_w(e, cmsys::Encoding::ToWide(file).c_str()); |
59 | | #else |
60 | 0 | archive_entry_copy_sourcepath(e, file.c_str()); |
61 | 0 | #endif |
62 | 0 | } |
63 | | |
64 | | class cmArchiveWrite::Entry |
65 | | { |
66 | | struct archive_entry* Object; |
67 | | |
68 | | public: |
69 | | Entry() |
70 | 0 | : Object(archive_entry_new()) |
71 | 0 | { |
72 | 0 | } |
73 | 0 | ~Entry() { archive_entry_free(this->Object); } |
74 | | Entry(Entry const&) = delete; |
75 | | Entry& operator=(Entry const&) = delete; |
76 | 0 | operator struct archive_entry *() { return this->Object; } |
77 | | }; |
78 | | |
79 | | struct cmArchiveWrite::Callback |
80 | | { |
81 | | // archive_write_callback |
82 | | static __LA_SSIZE_T Write(struct archive* /*unused*/, void* cd, |
83 | | void const* b, size_t n) |
84 | 0 | { |
85 | 0 | cmArchiveWrite* self = static_cast<cmArchiveWrite*>(cd); |
86 | 0 | if (self->Stream.write(static_cast<char const*>(b), |
87 | 0 | static_cast<std::streamsize>(n))) { |
88 | 0 | return static_cast<__LA_SSIZE_T>(n); |
89 | 0 | } |
90 | 0 | return static_cast<__LA_SSIZE_T>(-1); |
91 | 0 | } |
92 | | }; |
93 | | |
94 | | cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, |
95 | | std::string const& format, int compressionLevel, |
96 | | int numThreads) |
97 | 0 | : Stream(os) |
98 | 0 | , Archive(archive_write_new()) |
99 | 0 | , Disk(archive_read_disk_new()) |
100 | 0 | , Format(format) |
101 | 0 | { |
102 | | // Upstream fixed an issue with their integer parsing in 3.4.0 |
103 | | // which would cause spurious errors to be raised from `strtoull`. |
104 | |
|
105 | 0 | if (archive_write_set_format_by_name(this->Archive, format.c_str()) != |
106 | 0 | ARCHIVE_OK) { |
107 | 0 | this->Error = cmStrCat("archive_write_set_format_by_name: ", |
108 | 0 | cm_archive_error_string(this->Archive)); |
109 | 0 | return; |
110 | 0 | } |
111 | | |
112 | 0 | bool is7zip = (format == "7zip"); |
113 | 0 | bool isZip = (format == "zip"); |
114 | 0 | bool isFormatSupportsCompressionNatively = (is7zip || isZip); |
115 | |
|
116 | 0 | if (numThreads < 1) { |
117 | 0 | int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max() |
118 | 0 | : std::abs(numThreads); |
119 | |
|
120 | 0 | numThreads = |
121 | 0 | cm::clamp<int>(std::thread::hardware_concurrency(), 1, upperLimit); |
122 | 0 | } |
123 | |
|
124 | 0 | std::string sNumThreads = std::to_string(numThreads); |
125 | |
|
126 | 0 | if (!isFormatSupportsCompressionNatively) { |
127 | 0 | switch (c) { |
128 | 0 | case CompressNone: |
129 | 0 | if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) { |
130 | 0 | this->Error = cmStrCat("archive_write_add_filter_none: ", |
131 | 0 | cm_archive_error_string(this->Archive)); |
132 | 0 | return; |
133 | 0 | } |
134 | 0 | break; |
135 | 0 | case CompressCompress: |
136 | 0 | if (archive_write_add_filter_compress(this->Archive) != ARCHIVE_OK) { |
137 | 0 | this->Error = cmStrCat("archive_write_add_filter_compress: ", |
138 | 0 | cm_archive_error_string(this->Archive)); |
139 | 0 | return; |
140 | 0 | } |
141 | 0 | break; |
142 | 0 | case CompressGZip: { |
143 | 0 | if (archive_write_add_filter_gzip(this->Archive) != ARCHIVE_OK) { |
144 | 0 | this->Error = cmStrCat("archive_write_add_filter_gzip: ", |
145 | 0 | cm_archive_error_string(this->Archive)); |
146 | 0 | return; |
147 | 0 | } |
148 | 0 | std::string source_date_epoch; |
149 | 0 | cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch); |
150 | 0 | if (!source_date_epoch.empty()) { |
151 | | // We're not able to specify an arbitrary timestamp for gzip. |
152 | | // The next best thing is to omit the timestamp entirely. |
153 | 0 | if (archive_write_set_filter_option( |
154 | 0 | this->Archive, "gzip", "timestamp", nullptr) != ARCHIVE_OK) { |
155 | 0 | this->Error = cmStrCat("archive_write_set_filter_option: ", |
156 | 0 | cm_archive_error_string(this->Archive)); |
157 | 0 | return; |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } break; |
161 | 0 | case CompressBZip2: |
162 | 0 | if (archive_write_add_filter_bzip2(this->Archive) != ARCHIVE_OK) { |
163 | 0 | this->Error = cmStrCat("archive_write_add_filter_bzip2: ", |
164 | 0 | cm_archive_error_string(this->Archive)); |
165 | 0 | return; |
166 | 0 | } |
167 | 0 | break; |
168 | 0 | case CompressLZMA: |
169 | 0 | if (archive_write_add_filter_lzma(this->Archive) != ARCHIVE_OK) { |
170 | 0 | this->Error = cmStrCat("archive_write_add_filter_lzma: ", |
171 | 0 | cm_archive_error_string(this->Archive)); |
172 | 0 | return; |
173 | 0 | } |
174 | 0 | break; |
175 | 0 | case CompressXZ: |
176 | 0 | if (archive_write_add_filter_xz(this->Archive) != ARCHIVE_OK) { |
177 | 0 | this->Error = cmStrCat("archive_write_add_filter_xz: ", |
178 | 0 | cm_archive_error_string(this->Archive)); |
179 | 0 | return; |
180 | 0 | } |
181 | | |
182 | 0 | #if ARCHIVE_VERSION_NUMBER >= 3004000 |
183 | | |
184 | | # ifdef _AIX |
185 | | // FIXME: Using more than 2 threads creates an empty archive. |
186 | | // Enforce this limit pending further investigation. |
187 | | if (numThreads > 2) { |
188 | | numThreads = 2; |
189 | | sNumThreads = std::to_string(numThreads); |
190 | | } |
191 | | # endif |
192 | 0 | if (archive_write_set_filter_option(this->Archive, "xz", "threads", |
193 | 0 | sNumThreads.c_str()) != |
194 | 0 | ARCHIVE_OK) { |
195 | 0 | this->Error = cmStrCat("archive_compressor_xz_options: ", |
196 | 0 | cm_archive_error_string(this->Archive)); |
197 | 0 | return; |
198 | 0 | } |
199 | 0 | #endif |
200 | | |
201 | 0 | break; |
202 | 0 | case CompressZstd: |
203 | 0 | if (archive_write_add_filter_zstd(this->Archive) != ARCHIVE_OK) { |
204 | 0 | this->Error = cmStrCat("archive_write_add_filter_zstd: ", |
205 | 0 | cm_archive_error_string(this->Archive)); |
206 | 0 | return; |
207 | 0 | } |
208 | | |
209 | 0 | #if ARCHIVE_VERSION_NUMBER >= 3006000 |
210 | 0 | if (archive_write_set_filter_option(this->Archive, "zstd", "threads", |
211 | 0 | sNumThreads.c_str()) != |
212 | 0 | ARCHIVE_OK) { |
213 | 0 | this->Error = cmStrCat("archive_compressor_zstd_options: ", |
214 | 0 | cm_archive_error_string(this->Archive)); |
215 | 0 | return; |
216 | 0 | } |
217 | 0 | #endif |
218 | 0 | break; |
219 | 0 | case CompressPPMd: |
220 | 0 | this->Error = cmStrCat("PPMd is not supported for ", format); |
221 | 0 | return; |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | 0 | if (isFormatSupportsCompressionNatively || compressionLevel != 0) { |
226 | 0 | std::string compressionLevelStr = std::to_string(compressionLevel); |
227 | 0 | std::string archiveFilterName; |
228 | 0 | switch (c) { |
229 | 0 | case CompressNone: |
230 | 0 | if (is7zip || isZip) { |
231 | 0 | archiveFilterName = "store"; |
232 | 0 | } else { |
233 | | // Nothing to do - the value should be empty |
234 | 0 | } |
235 | 0 | break; |
236 | 0 | case CompressCompress: |
237 | 0 | if (is7zip || isZip) { |
238 | 0 | this->Error = |
239 | 0 | cmStrCat("CompressCompress is not supported for ", format); |
240 | 0 | } else { |
241 | | // Nothing to do - the value should be empty |
242 | 0 | } |
243 | 0 | break; |
244 | 0 | case CompressGZip: |
245 | 0 | if (is7zip || isZip) { |
246 | 0 | archiveFilterName = "deflate"; |
247 | 0 | } else { |
248 | 0 | archiveFilterName = "gzip"; |
249 | 0 | } |
250 | 0 | break; |
251 | 0 | case CompressBZip2: |
252 | | #if ARCHIVE_VERSION_NUMBER < 3008000 |
253 | | if (isZip) { |
254 | | this->Error = cmStrCat("BZip2 is not supported for ", format, |
255 | | ". Please, build CMake with libarchive 3.8.0 " |
256 | | "or newer if you want to use it."); |
257 | | return; |
258 | | } |
259 | | #endif |
260 | 0 | archiveFilterName = "bzip2"; |
261 | 0 | break; |
262 | 0 | case CompressLZMA: |
263 | | #if ARCHIVE_VERSION_NUMBER < 3008000 |
264 | | if (isZip) { |
265 | | this->Error = cmStrCat("LZMA is not supported for ", format, |
266 | | ". Please, build CMake with libarchive 3.8.0 " |
267 | | "or newer if you want to use it."); |
268 | | return; |
269 | | } |
270 | | #endif |
271 | 0 | if (is7zip) { |
272 | 0 | archiveFilterName = "lzma1"; |
273 | 0 | } else { |
274 | 0 | archiveFilterName = "lzma"; |
275 | 0 | } |
276 | 0 | break; |
277 | 0 | case CompressXZ: |
278 | | #if ARCHIVE_VERSION_NUMBER < 3008000 |
279 | | if (isZip) { |
280 | | this->Error = cmStrCat("LZMA2 (XZ) is not supported for ", format, |
281 | | ". Please, build CMake with libarchive 3.8.0 " |
282 | | "or newer if you want to use it."); |
283 | | return; |
284 | | } |
285 | | #endif |
286 | 0 | if (is7zip) { |
287 | 0 | archiveFilterName = "lzma2"; |
288 | 0 | } else { |
289 | 0 | archiveFilterName = "xz"; |
290 | 0 | } |
291 | 0 | break; |
292 | 0 | case CompressZstd: |
293 | | #if ARCHIVE_VERSION_NUMBER < 3008000 |
294 | | if (is7zip || isZip) { |
295 | | this->Error = cmStrCat("Zstd is not supported for ", format, |
296 | | ". Please, build CMake with libarchive 3.8.0 " |
297 | | "or newer if you want to use it."); |
298 | | return; |
299 | | } |
300 | | #endif |
301 | 0 | archiveFilterName = "zstd"; |
302 | 0 | break; |
303 | 0 | case CompressPPMd: |
304 | 0 | if (is7zip) { |
305 | 0 | archiveFilterName = "ppmd"; |
306 | 0 | } else { |
307 | 0 | this->Error = cmStrCat("PPMd is not supported for ", format); |
308 | 0 | } |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | 0 | if (isFormatSupportsCompressionNatively) { |
313 | 0 | if (archiveFilterName.empty()) { |
314 | 0 | this->Error = cmStrCat("Unknown compression method for ", format); |
315 | 0 | return; |
316 | 0 | } |
317 | | |
318 | 0 | if (archive_write_set_format_option( |
319 | 0 | this->Archive, format.c_str(), "compression", |
320 | 0 | archiveFilterName.c_str()) != ARCHIVE_OK) { |
321 | 0 | this->Error = |
322 | 0 | cmStrCat("archive_write_set_format_option(compression): ", |
323 | 0 | cm_archive_error_string(this->Archive)); |
324 | 0 | return; |
325 | 0 | } |
326 | | |
327 | 0 | #if ARCHIVE_VERSION_NUMBER >= 3008000 |
328 | 0 | if (archive_write_set_format_option(this->Archive, format.c_str(), |
329 | 0 | "threads", |
330 | 0 | sNumThreads.c_str()) != ARCHIVE_OK) { |
331 | 0 | this->Error = cmStrCat("archive_write_set_format_option(threads): ", |
332 | 0 | cm_archive_error_string(this->Archive)); |
333 | 0 | return; |
334 | 0 | } |
335 | 0 | #endif |
336 | | |
337 | 0 | if (compressionLevel != 0) { |
338 | 0 | if (archive_write_set_format_option( |
339 | 0 | this->Archive, format.c_str(), "compression-level", |
340 | 0 | compressionLevelStr.c_str()) != ARCHIVE_OK) { |
341 | 0 | this->Error = |
342 | 0 | cmStrCat("archive_write_set_format_option(compression-level): ", |
343 | 0 | cm_archive_error_string(this->Archive)); |
344 | 0 | return; |
345 | 0 | } |
346 | 0 | } |
347 | 0 | } else if (compressionLevel != 0 && !archiveFilterName.empty()) { |
348 | 0 | if (archive_write_set_filter_option( |
349 | 0 | this->Archive, archiveFilterName.c_str(), "compression-level", |
350 | 0 | compressionLevelStr.c_str()) != ARCHIVE_OK) { |
351 | 0 | this->Error = cmStrCat("archive_write_set_filter_option: ", |
352 | 0 | cm_archive_error_string(this->Archive)); |
353 | 0 | return; |
354 | 0 | } |
355 | 0 | } |
356 | 0 | } |
357 | | |
358 | 0 | #if !defined(_WIN32) || defined(__CYGWIN__) |
359 | 0 | if (archive_read_disk_set_standard_lookup(this->Disk) != ARCHIVE_OK) { |
360 | 0 | this->Error = cmStrCat("archive_read_disk_set_standard_lookup: ", |
361 | 0 | cm_archive_error_string(this->Archive)); |
362 | 0 | return; |
363 | 0 | } |
364 | 0 | #endif |
365 | | |
366 | | // do not pad the last block!! |
367 | 0 | if (archive_write_set_bytes_in_last_block(this->Archive, 1)) { |
368 | 0 | this->Error = cmStrCat("archive_write_set_bytes_in_last_block: ", |
369 | 0 | cm_archive_error_string(this->Archive)); |
370 | 0 | return; |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | bool cmArchiveWrite::Open() |
375 | 0 | { |
376 | 0 | if (!this->Error.empty()) { |
377 | 0 | return false; |
378 | 0 | } |
379 | 0 | if (archive_write_open( |
380 | 0 | this->Archive, this, nullptr, |
381 | 0 | reinterpret_cast<archive_write_callback*>(&Callback::Write), |
382 | 0 | nullptr) != ARCHIVE_OK) { |
383 | 0 | this->Error = |
384 | 0 | cmStrCat("archive_write_open: ", cm_archive_error_string(this->Archive)); |
385 | 0 | return false; |
386 | 0 | } |
387 | 0 | return true; |
388 | 0 | } |
389 | | |
390 | | cmArchiveWrite::~cmArchiveWrite() |
391 | 0 | { |
392 | 0 | archive_read_free(this->Disk); |
393 | 0 | archive_write_free(this->Archive); |
394 | 0 | } |
395 | | |
396 | | bool cmArchiveWrite::Add(std::string path, size_t skip, char const* prefix, |
397 | | bool recursive) |
398 | 0 | { |
399 | 0 | if (!path.empty() && path.back() == '/') { |
400 | 0 | path.erase(path.size() - 1); |
401 | 0 | } |
402 | 0 | this->AddPath(path, skip, prefix, recursive); |
403 | 0 | return this->Okay(); |
404 | 0 | } |
405 | | |
406 | | bool cmArchiveWrite::AddPath(std::string const& path, size_t skip, |
407 | | char const* prefix, bool recursive) |
408 | 0 | { |
409 | 0 | if (path != "." || (this->Format != "zip" && this->Format != "7zip")) { |
410 | 0 | if (!this->AddFile(path, skip, prefix)) { |
411 | 0 | return false; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | if ((!cmSystemTools::FileIsDirectory(path) || !recursive) || |
415 | 0 | cmSystemTools::FileIsSymlink(path)) { |
416 | 0 | return true; |
417 | 0 | } |
418 | 0 | cmsys::Directory d; |
419 | 0 | if (d.Load(path)) { |
420 | 0 | std::string next = cmStrCat(path, '/'); |
421 | 0 | if (next == "./" && (this->Format == "zip" || this->Format == "7zip")) { |
422 | 0 | next.clear(); |
423 | 0 | } |
424 | 0 | std::string::size_type end = next.size(); |
425 | 0 | unsigned long n = d.GetNumberOfFiles(); |
426 | 0 | for (unsigned long i = 0; i < n; ++i) { |
427 | 0 | std::string const& file = d.GetFileName(i); |
428 | 0 | if (file != "." && file != "..") { |
429 | 0 | next.erase(end); |
430 | 0 | next += file; |
431 | 0 | if (!this->AddPath(next, skip, prefix)) { |
432 | 0 | return false; |
433 | 0 | } |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | 0 | return true; |
438 | 0 | } |
439 | | |
440 | | bool cmArchiveWrite::AddFile(std::string const& file, size_t skip, |
441 | | char const* prefix) |
442 | 0 | { |
443 | 0 | this->Error = ""; |
444 | | // Skip the file if we have no name for it. This may happen on a |
445 | | // top-level directory, which does not need to be included anyway. |
446 | 0 | if (skip >= file.length()) { |
447 | 0 | return true; |
448 | 0 | } |
449 | 0 | cm::string_view out = cm::string_view(file).substr(skip); |
450 | | |
451 | | // Meta-data. |
452 | 0 | std::string dest = cmStrCat(prefix ? prefix : "", out); |
453 | 0 | if (this->Verbose) { |
454 | 0 | std::cout << dest << "\n"; |
455 | 0 | } |
456 | 0 | Entry e; |
457 | 0 | cm_archive_entry_copy_sourcepath(e, file); |
458 | 0 | cm_archive_entry_copy_pathname(e, dest.c_str()); |
459 | 0 | if (archive_read_disk_entry_from_file(this->Disk, e, -1, nullptr) != |
460 | 0 | ARCHIVE_OK) { |
461 | 0 | this->Error = |
462 | 0 | cmStrCat("Unable to read from file:\n ", file, "\nbecause:\n ", |
463 | 0 | cm_archive_error_string(this->Disk)); |
464 | 0 | return false; |
465 | 0 | } |
466 | 0 | if (!this->MTime.empty()) { |
467 | 0 | time_t now; |
468 | 0 | time(&now); |
469 | 0 | time_t t = cm_parse_date(now, this->MTime.c_str()); |
470 | 0 | if (t == -1) { |
471 | 0 | this->Error = cmStrCat("unable to parse mtime '", this->MTime, '\''); |
472 | 0 | return false; |
473 | 0 | } |
474 | 0 | archive_entry_set_mtime(e, t, 0); |
475 | 0 | } else { |
476 | 0 | std::string source_date_epoch; |
477 | 0 | cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch); |
478 | 0 | if (!source_date_epoch.empty()) { |
479 | 0 | std::istringstream iss(source_date_epoch); |
480 | 0 | time_t epochTime; |
481 | 0 | iss >> epochTime; |
482 | 0 | if (iss.eof() && !iss.fail()) { |
483 | | // Set all of the file times to the epoch time to handle archive |
484 | | // formats that include creation/access time. |
485 | 0 | archive_entry_set_mtime(e, epochTime, 0); |
486 | 0 | archive_entry_set_atime(e, epochTime, 0); |
487 | 0 | archive_entry_set_ctime(e, epochTime, 0); |
488 | 0 | archive_entry_set_birthtime(e, epochTime, 0); |
489 | 0 | } |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | // manages the uid/guid of the entry (if any) |
494 | 0 | if (this->Uid.IsSet() && this->Gid.IsSet()) { |
495 | 0 | archive_entry_set_uid(e, this->Uid.Get()); |
496 | 0 | archive_entry_set_gid(e, this->Gid.Get()); |
497 | 0 | } |
498 | |
|
499 | 0 | if (!this->Uname.empty() && !this->Gname.empty()) { |
500 | 0 | archive_entry_set_uname(e, this->Uname.c_str()); |
501 | 0 | archive_entry_set_gname(e, this->Gname.c_str()); |
502 | 0 | } |
503 | | |
504 | | // manages the permissions |
505 | 0 | if (this->Permissions.IsSet()) { |
506 | 0 | archive_entry_set_perm(e, this->Permissions.Get()); |
507 | 0 | } |
508 | |
|
509 | 0 | if (this->PermissionsMask.IsSet()) { |
510 | 0 | int perm = archive_entry_perm(e); |
511 | 0 | archive_entry_set_perm(e, perm & this->PermissionsMask.Get()); |
512 | 0 | } |
513 | | |
514 | | // Clear acl and xattr fields not useful for distribution. |
515 | 0 | archive_entry_acl_clear(e); |
516 | 0 | archive_entry_xattr_clear(e); |
517 | 0 | archive_entry_set_fflags(e, 0, 0); |
518 | |
|
519 | 0 | if (this->Format == "pax" || this->Format == "paxr") { |
520 | | // Sparse files are a GNU tar extension. |
521 | | // Do not use them in standard tar files. |
522 | 0 | archive_entry_sparse_clear(e); |
523 | 0 | } |
524 | |
|
525 | 0 | if (archive_write_header(this->Archive, e) != ARCHIVE_OK) { |
526 | 0 | this->Error = cmStrCat("archive_write_header: ", |
527 | 0 | cm_archive_error_string(this->Archive)); |
528 | 0 | return false; |
529 | 0 | } |
530 | | |
531 | | // do not copy content of symlink |
532 | 0 | if (!archive_entry_symlink(e)) { |
533 | | // Content. |
534 | 0 | if (size_t size = static_cast<size_t>(archive_entry_size(e))) { |
535 | 0 | return this->AddData(file, size); |
536 | 0 | } |
537 | 0 | } |
538 | 0 | return true; |
539 | 0 | } |
540 | | |
541 | | bool cmArchiveWrite::AddData(std::string const& file, size_t size) |
542 | 0 | { |
543 | 0 | cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary); |
544 | 0 | if (!fin) { |
545 | 0 | this->Error = cmStrCat("Error opening \"", file, |
546 | 0 | "\": ", cmSystemTools::GetLastSystemError()); |
547 | 0 | return false; |
548 | 0 | } |
549 | | |
550 | 0 | char buffer[16384]; |
551 | 0 | size_t nleft = size; |
552 | 0 | while (nleft > 0) { |
553 | 0 | using ssize_type = std::streamsize; |
554 | 0 | size_t const nnext = nleft > sizeof(buffer) ? sizeof(buffer) : nleft; |
555 | 0 | ssize_type const nnext_s = static_cast<ssize_type>(nnext); |
556 | 0 | fin.read(buffer, nnext_s); |
557 | | // Some stream libraries (older HPUX) return failure at end of |
558 | | // file on the last read even if some data were read. Check |
559 | | // gcount instead of trusting the stream error status. |
560 | 0 | if (static_cast<size_t>(fin.gcount()) != nnext) { |
561 | 0 | break; |
562 | 0 | } |
563 | 0 | if (archive_write_data(this->Archive, buffer, nnext) != nnext_s) { |
564 | 0 | this->Error = cmStrCat("archive_write_data: ", |
565 | 0 | cm_archive_error_string(this->Archive)); |
566 | 0 | return false; |
567 | 0 | } |
568 | 0 | nleft -= nnext; |
569 | 0 | } |
570 | 0 | if (nleft > 0) { |
571 | 0 | this->Error = cmStrCat("Error reading \"", file, |
572 | 0 | "\": ", cmSystemTools::GetLastSystemError()); |
573 | 0 | return false; |
574 | 0 | } |
575 | 0 | return true; |
576 | 0 | } |