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