1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3
4 #include "cmFileCopier.h"
5
6 #include "cmsys/Directory.hxx"
7 #include "cmsys/Glob.hxx"
8
9 #include "cmExecutionStatus.h"
10 #include "cmFSPermissions.h"
11 #include "cmFileTimes.h"
12 #include "cmMakefile.h"
13 #include "cmStringAlgorithms.h"
14 #include "cmSystemTools.h"
15 #include "cmValue.h"
16
17 #ifdef _WIN32
18 # include "cmsys/FStream.hxx"
19 #endif
20
21 #include <cstring>
22 #include <sstream>
23
24 using namespace cmFSPermissions;
25
cmFileCopier(cmExecutionStatus & status,const char * name)26 cmFileCopier::cmFileCopier(cmExecutionStatus& status, const char* name)
27 : Status(status)
28 , Makefile(&status.GetMakefile())
29 , Name(name)
30 , Always(false)
31 , MatchlessFiles(true)
32 , FilePermissions(0)
33 , DirPermissions(0)
34 , CurrentMatchRule(nullptr)
35 , UseGivenPermissionsFile(false)
36 , UseGivenPermissionsDir(false)
37 , UseSourcePermissions(true)
38 , FollowSymlinkChain(false)
39 , Doing(DoingNone)
40 {
41 }
42
43 cmFileCopier::~cmFileCopier() = default;
44
CollectMatchProperties(const std::string & file)45 cmFileCopier::MatchProperties cmFileCopier::CollectMatchProperties(
46 const std::string& file)
47 {
48 // Match rules are case-insensitive on some platforms.
49 #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
50 const std::string file_to_match = cmSystemTools::LowerCase(file);
51 #else
52 const std::string& file_to_match = file;
53 #endif
54
55 // Collect properties from all matching rules.
56 bool matched = false;
57 MatchProperties result;
58 for (MatchRule& mr : this->MatchRules) {
59 if (mr.Regex.find(file_to_match)) {
60 matched = true;
61 result.Exclude |= mr.Properties.Exclude;
62 result.Permissions |= mr.Properties.Permissions;
63 }
64 }
65 if (!matched && !this->MatchlessFiles) {
66 result.Exclude = !cmSystemTools::FileIsDirectory(file);
67 }
68 return result;
69 }
70
SetPermissions(const std::string & toFile,mode_t permissions)71 bool cmFileCopier::SetPermissions(const std::string& toFile,
72 mode_t permissions)
73 {
74 if (permissions) {
75 #ifdef _WIN32
76 if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
77 // Store the mode in an NTFS alternate stream.
78 std::string mode_t_adt_filename = toFile + ":cmake_mode_t";
79
80 // Writing to an NTFS alternate stream changes the modification
81 // time, so we need to save and restore its original value.
82 cmFileTimes file_time_orig(toFile);
83 {
84 cmsys::ofstream permissionStream(mode_t_adt_filename.c_str());
85 if (permissionStream) {
86 permissionStream << std::oct << permissions << std::endl;
87 }
88 permissionStream.close();
89 }
90 file_time_orig.Store(toFile);
91 }
92 #endif
93
94 if (!cmSystemTools::SetPermissions(toFile, permissions)) {
95 std::ostringstream e;
96 e << this->Name << " cannot set permissions on \"" << toFile
97 << "\": " << cmSystemTools::GetLastSystemError() << ".";
98 this->Status.SetError(e.str());
99 return false;
100 }
101 }
102 return true;
103 }
104
105 // Translate an argument to a permissions bit.
CheckPermissions(std::string const & arg,mode_t & permissions)106 bool cmFileCopier::CheckPermissions(std::string const& arg,
107 mode_t& permissions)
108 {
109 if (!cmFSPermissions::stringToModeT(arg, permissions)) {
110 std::ostringstream e;
111 e << this->Name << " given invalid permission \"" << arg << "\".";
112 this->Status.SetError(e.str());
113 return false;
114 }
115 return true;
116 }
117
ToName(std::string const & fromName)118 std::string const& cmFileCopier::ToName(std::string const& fromName)
119 {
120 return fromName;
121 }
122
ReportMissing(const std::string & fromFile)123 bool cmFileCopier::ReportMissing(const std::string& fromFile)
124 {
125 // The input file does not exist and installation is not optional.
126 std::ostringstream e;
127 e << this->Name << " cannot find \"" << fromFile
128 << "\": " << cmSystemTools::GetLastSystemError() << ".";
129 this->Status.SetError(e.str());
130 return false;
131 }
132
NotBeforeMatch(std::string const & arg)133 void cmFileCopier::NotBeforeMatch(std::string const& arg)
134 {
135 std::ostringstream e;
136 e << "option " << arg << " may not appear before PATTERN or REGEX.";
137 this->Status.SetError(e.str());
138 this->Doing = DoingError;
139 }
140
NotAfterMatch(std::string const & arg)141 void cmFileCopier::NotAfterMatch(std::string const& arg)
142 {
143 std::ostringstream e;
144 e << "option " << arg << " may not appear after PATTERN or REGEX.";
145 this->Status.SetError(e.str());
146 this->Doing = DoingError;
147 }
148
DefaultFilePermissions()149 void cmFileCopier::DefaultFilePermissions()
150 {
151 // Use read/write permissions.
152 this->FilePermissions = 0;
153 this->FilePermissions |= mode_owner_read;
154 this->FilePermissions |= mode_owner_write;
155 this->FilePermissions |= mode_group_read;
156 this->FilePermissions |= mode_world_read;
157 }
158
DefaultDirectoryPermissions()159 void cmFileCopier::DefaultDirectoryPermissions()
160 {
161 // Use read/write/executable permissions.
162 this->DirPermissions = 0;
163 this->DirPermissions |= mode_owner_read;
164 this->DirPermissions |= mode_owner_write;
165 this->DirPermissions |= mode_owner_execute;
166 this->DirPermissions |= mode_group_read;
167 this->DirPermissions |= mode_group_execute;
168 this->DirPermissions |= mode_world_read;
169 this->DirPermissions |= mode_world_execute;
170 }
171
GetDefaultDirectoryPermissions(mode_t ** mode)172 bool cmFileCopier::GetDefaultDirectoryPermissions(mode_t** mode)
173 {
174 // check if default dir creation permissions were set
175 cmValue default_dir_install_permissions = this->Makefile->GetDefinition(
176 "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
177 if (cmNonempty(default_dir_install_permissions)) {
178 std::vector<std::string> items =
179 cmExpandedList(*default_dir_install_permissions);
180 for (const auto& arg : items) {
181 if (!this->CheckPermissions(arg, **mode)) {
182 this->Status.SetError(
183 " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS variable.");
184 return false;
185 }
186 }
187 } else {
188 *mode = nullptr;
189 }
190
191 return true;
192 }
193
Parse(std::vector<std::string> const & args)194 bool cmFileCopier::Parse(std::vector<std::string> const& args)
195 {
196 this->Doing = DoingFiles;
197 for (unsigned int i = 1; i < args.size(); ++i) {
198 // Check this argument.
199 if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) {
200 std::ostringstream e;
201 e << "called with unknown argument \"" << args[i] << "\".";
202 this->Status.SetError(e.str());
203 return false;
204 }
205
206 // Quit if an argument is invalid.
207 if (this->Doing == DoingError) {
208 return false;
209 }
210 }
211
212 // Require a destination.
213 if (this->Destination.empty()) {
214 std::ostringstream e;
215 e << this->Name << " given no DESTINATION";
216 this->Status.SetError(e.str());
217 return false;
218 }
219
220 // If file permissions were not specified set default permissions.
221 if (!this->UseGivenPermissionsFile && !this->UseSourcePermissions) {
222 this->DefaultFilePermissions();
223 }
224
225 // If directory permissions were not specified set default permissions.
226 if (!this->UseGivenPermissionsDir && !this->UseSourcePermissions) {
227 this->DefaultDirectoryPermissions();
228 }
229
230 return true;
231 }
232
CheckKeyword(std::string const & arg)233 bool cmFileCopier::CheckKeyword(std::string const& arg)
234 {
235 if (arg == "DESTINATION") {
236 if (this->CurrentMatchRule) {
237 this->NotAfterMatch(arg);
238 } else {
239 this->Doing = DoingDestination;
240 }
241 } else if (arg == "FILES_FROM_DIR") {
242 if (this->CurrentMatchRule) {
243 this->NotAfterMatch(arg);
244 } else {
245 this->Doing = DoingFilesFromDir;
246 }
247 } else if (arg == "PATTERN") {
248 this->Doing = DoingPattern;
249 } else if (arg == "REGEX") {
250 this->Doing = DoingRegex;
251 } else if (arg == "FOLLOW_SYMLINK_CHAIN") {
252 this->FollowSymlinkChain = true;
253 this->Doing = DoingNone;
254 } else if (arg == "EXCLUDE") {
255 // Add this property to the current match rule.
256 if (this->CurrentMatchRule) {
257 this->CurrentMatchRule->Properties.Exclude = true;
258 this->Doing = DoingNone;
259 } else {
260 this->NotBeforeMatch(arg);
261 }
262 } else if (arg == "PERMISSIONS") {
263 if (this->CurrentMatchRule) {
264 this->Doing = DoingPermissionsMatch;
265 } else {
266 this->NotBeforeMatch(arg);
267 }
268 } else if (arg == "FILE_PERMISSIONS") {
269 if (this->CurrentMatchRule) {
270 this->NotAfterMatch(arg);
271 } else {
272 this->Doing = DoingPermissionsFile;
273 this->UseGivenPermissionsFile = true;
274 }
275 } else if (arg == "DIRECTORY_PERMISSIONS") {
276 if (this->CurrentMatchRule) {
277 this->NotAfterMatch(arg);
278 } else {
279 this->Doing = DoingPermissionsDir;
280 this->UseGivenPermissionsDir = true;
281 }
282 } else if (arg == "USE_SOURCE_PERMISSIONS") {
283 if (this->CurrentMatchRule) {
284 this->NotAfterMatch(arg);
285 } else {
286 this->Doing = DoingNone;
287 this->UseSourcePermissions = true;
288 }
289 } else if (arg == "NO_SOURCE_PERMISSIONS") {
290 if (this->CurrentMatchRule) {
291 this->NotAfterMatch(arg);
292 } else {
293 this->Doing = DoingNone;
294 this->UseSourcePermissions = false;
295 }
296 } else if (arg == "FILES_MATCHING") {
297 if (this->CurrentMatchRule) {
298 this->NotAfterMatch(arg);
299 } else {
300 this->Doing = DoingNone;
301 this->MatchlessFiles = false;
302 }
303 } else {
304 return false;
305 }
306 return true;
307 }
308
CheckValue(std::string const & arg)309 bool cmFileCopier::CheckValue(std::string const& arg)
310 {
311 switch (this->Doing) {
312 case DoingFiles:
313 this->Files.push_back(arg);
314 break;
315 case DoingDestination:
316 if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) {
317 this->Destination = arg;
318 } else {
319 this->Destination =
320 cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', arg);
321 }
322 this->Doing = DoingNone;
323 break;
324 case DoingFilesFromDir:
325 if (cmSystemTools::FileIsFullPath(arg)) {
326 this->FilesFromDir = arg;
327 } else {
328 this->FilesFromDir =
329 cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', arg);
330 }
331 cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir);
332 this->Doing = DoingNone;
333 break;
334 case DoingPattern: {
335 // Convert the pattern to a regular expression. Require a
336 // leading slash and trailing end-of-string in the matched
337 // string to make sure the pattern matches only whole file
338 // names.
339 std::string regex =
340 cmStrCat('/', cmsys::Glob::PatternToRegex(arg, false), '$');
341 this->MatchRules.emplace_back(regex);
342 this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
343 if (this->CurrentMatchRule->Regex.is_valid()) {
344 this->Doing = DoingNone;
345 } else {
346 std::ostringstream e;
347 e << "could not compile PATTERN \"" << arg << "\".";
348 this->Status.SetError(e.str());
349 this->Doing = DoingError;
350 }
351 } break;
352 case DoingRegex:
353 this->MatchRules.emplace_back(arg);
354 this->CurrentMatchRule = &*(this->MatchRules.end() - 1);
355 if (this->CurrentMatchRule->Regex.is_valid()) {
356 this->Doing = DoingNone;
357 } else {
358 std::ostringstream e;
359 e << "could not compile REGEX \"" << arg << "\".";
360 this->Status.SetError(e.str());
361 this->Doing = DoingError;
362 }
363 break;
364 case DoingPermissionsFile:
365 if (!this->CheckPermissions(arg, this->FilePermissions)) {
366 this->Doing = DoingError;
367 }
368 break;
369 case DoingPermissionsDir:
370 if (!this->CheckPermissions(arg, this->DirPermissions)) {
371 this->Doing = DoingError;
372 }
373 break;
374 case DoingPermissionsMatch:
375 if (!this->CheckPermissions(
376 arg, this->CurrentMatchRule->Properties.Permissions)) {
377 this->Doing = DoingError;
378 }
379 break;
380 default:
381 return false;
382 }
383 return true;
384 }
385
Run(std::vector<std::string> const & args)386 bool cmFileCopier::Run(std::vector<std::string> const& args)
387 {
388 if (!this->Parse(args)) {
389 return false;
390 }
391
392 for (std::string const& f : this->Files) {
393 std::string file;
394 if (!f.empty() && !cmSystemTools::FileIsFullPath(f)) {
395 if (!this->FilesFromDir.empty()) {
396 file = this->FilesFromDir;
397 } else {
398 file = this->Makefile->GetCurrentSourceDirectory();
399 }
400 file += "/";
401 file += f;
402 } else if (!this->FilesFromDir.empty()) {
403 this->Status.SetError("option FILES_FROM_DIR requires all files "
404 "to be specified as relative paths.");
405 return false;
406 } else {
407 file = f;
408 }
409
410 // Split the input file into its directory and name components.
411 std::vector<std::string> fromPathComponents;
412 cmSystemTools::SplitPath(file, fromPathComponents);
413 std::string fromName = *(fromPathComponents.end() - 1);
414 std::string fromDir = cmSystemTools::JoinPath(
415 fromPathComponents.begin(), fromPathComponents.end() - 1);
416
417 // Compute the full path to the destination file.
418 std::string toFile = this->Destination;
419 if (!this->FilesFromDir.empty()) {
420 std::string dir = cmSystemTools::GetFilenamePath(f);
421 if (!dir.empty()) {
422 toFile += "/";
423 toFile += dir;
424 }
425 }
426 std::string const& toName = this->ToName(fromName);
427 if (!toName.empty()) {
428 toFile += "/";
429 toFile += toName;
430 }
431
432 // Construct the full path to the source file. The file name may
433 // have been changed above.
434 std::string fromFile = fromDir;
435 if (!fromName.empty()) {
436 fromFile += "/";
437 fromFile += fromName;
438 }
439
440 if (!this->Install(fromFile, toFile)) {
441 return false;
442 }
443 }
444 return true;
445 }
446
Install(const std::string & fromFile,const std::string & toFile)447 bool cmFileCopier::Install(const std::string& fromFile,
448 const std::string& toFile)
449 {
450 if (fromFile.empty()) {
451 this->Status.SetError(
452 "INSTALL encountered an empty string input file name.");
453 return false;
454 }
455
456 // Collect any properties matching this file name.
457 MatchProperties match_properties = this->CollectMatchProperties(fromFile);
458
459 // Skip the file if it is excluded.
460 if (match_properties.Exclude) {
461 return true;
462 }
463
464 if (cmSystemTools::SameFile(fromFile, toFile)) {
465 return true;
466 }
467
468 std::string newFromFile = fromFile;
469 std::string newToFile = toFile;
470
471 if (this->FollowSymlinkChain &&
472 !this->InstallSymlinkChain(newFromFile, newToFile)) {
473 return false;
474 }
475
476 if (cmSystemTools::FileIsSymlink(newFromFile)) {
477 return this->InstallSymlink(newFromFile, newToFile);
478 }
479 if (cmSystemTools::FileIsDirectory(newFromFile)) {
480 return this->InstallDirectory(newFromFile, newToFile, match_properties);
481 }
482 if (cmSystemTools::FileExists(newFromFile)) {
483 return this->InstallFile(newFromFile, newToFile, match_properties);
484 }
485 return this->ReportMissing(newFromFile);
486 }
487
InstallSymlinkChain(std::string & fromFile,std::string & toFile)488 bool cmFileCopier::InstallSymlinkChain(std::string& fromFile,
489 std::string& toFile)
490 {
491 std::string newFromFile;
492 std::string toFilePath = cmSystemTools::GetFilenamePath(toFile);
493 while (cmSystemTools::ReadSymlink(fromFile, newFromFile)) {
494 if (!cmSystemTools::FileIsFullPath(newFromFile)) {
495 std::string fromFilePath = cmSystemTools::GetFilenamePath(fromFile);
496 newFromFile = cmStrCat(fromFilePath, "/", newFromFile);
497 }
498
499 std::string symlinkTarget = cmSystemTools::GetFilenameName(newFromFile);
500
501 bool copy = true;
502 if (!this->Always) {
503 std::string oldSymlinkTarget;
504 if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
505 if (symlinkTarget == oldSymlinkTarget) {
506 copy = false;
507 }
508 }
509 }
510
511 this->ReportCopy(toFile, TypeLink, copy);
512
513 if (copy) {
514 cmSystemTools::RemoveFile(toFile);
515 cmSystemTools::MakeDirectory(toFilePath);
516
517 if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
518 std::ostringstream e;
519 e << this->Name << " cannot create symlink \"" << toFile
520 << "\": " << cmSystemTools::GetLastSystemError() << ".";
521 this->Status.SetError(e.str());
522 return false;
523 }
524 }
525
526 fromFile = newFromFile;
527 toFile = cmStrCat(toFilePath, "/", symlinkTarget);
528 }
529
530 return true;
531 }
532
InstallSymlink(const std::string & fromFile,const std::string & toFile)533 bool cmFileCopier::InstallSymlink(const std::string& fromFile,
534 const std::string& toFile)
535 {
536 // Read the original symlink.
537 std::string symlinkTarget;
538 if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) {
539 std::ostringstream e;
540 e << this->Name << " cannot read symlink \"" << fromFile
541 << "\" to duplicate at \"" << toFile
542 << "\": " << cmSystemTools::GetLastSystemError() << ".";
543 this->Status.SetError(e.str());
544 return false;
545 }
546
547 // Compare the symlink value to that at the destination if not
548 // always installing.
549 bool copy = true;
550 if (!this->Always) {
551 std::string oldSymlinkTarget;
552 if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
553 if (symlinkTarget == oldSymlinkTarget) {
554 copy = false;
555 }
556 }
557 }
558
559 // Inform the user about this file installation.
560 this->ReportCopy(toFile, TypeLink, copy);
561
562 if (copy) {
563 // Remove the destination file so we can always create the symlink.
564 cmSystemTools::RemoveFile(toFile);
565
566 // Create destination directory if it doesn't exist
567 cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
568
569 // Create the symlink.
570 if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
571 std::ostringstream e;
572 e << this->Name << " cannot duplicate symlink \"" << fromFile
573 << "\" at \"" << toFile
574 << "\": " << cmSystemTools::GetLastSystemError() << ".";
575 this->Status.SetError(e.str());
576 return false;
577 }
578 }
579
580 return true;
581 }
582
InstallFile(const std::string & fromFile,const std::string & toFile,MatchProperties match_properties)583 bool cmFileCopier::InstallFile(const std::string& fromFile,
584 const std::string& toFile,
585 MatchProperties match_properties)
586 {
587 // Determine whether we will copy the file.
588 bool copy = true;
589 if (!this->Always) {
590 // If both files exist with the same time do not copy.
591 if (!this->FileTimes.DifferS(fromFile, toFile)) {
592 copy = false;
593 }
594 }
595
596 // Inform the user about this file installation.
597 this->ReportCopy(toFile, TypeFile, copy);
598
599 // Copy the file.
600 if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) {
601 std::ostringstream e;
602 e << this->Name << " cannot copy file \"" << fromFile << "\" to \""
603 << toFile << "\": " << cmSystemTools::GetLastSystemError() << ".";
604 this->Status.SetError(e.str());
605 return false;
606 }
607
608 // Set the file modification time of the destination file.
609 if (copy && !this->Always) {
610 // Add write permission so we can set the file time.
611 // Permissions are set unconditionally below anyway.
612 mode_t perm = 0;
613 if (cmSystemTools::GetPermissions(toFile, perm)) {
614 cmSystemTools::SetPermissions(toFile, perm | mode_owner_write);
615 }
616 if (!cmFileTimes::Copy(fromFile, toFile)) {
617 std::ostringstream e;
618 e << this->Name << " cannot set modification time on \"" << toFile
619 << "\": " << cmSystemTools::GetLastSystemError() << ".";
620 this->Status.SetError(e.str());
621 return false;
622 }
623 }
624
625 // Set permissions of the destination file.
626 mode_t permissions =
627 (match_properties.Permissions ? match_properties.Permissions
628 : this->FilePermissions);
629 if (!permissions) {
630 // No permissions were explicitly provided but the user requested
631 // that the source file permissions be used.
632 cmSystemTools::GetPermissions(fromFile, permissions);
633 }
634 return this->SetPermissions(toFile, permissions);
635 }
636
InstallDirectory(const std::string & source,const std::string & destination,MatchProperties match_properties)637 bool cmFileCopier::InstallDirectory(const std::string& source,
638 const std::string& destination,
639 MatchProperties match_properties)
640 {
641 // Inform the user about this directory installation.
642 this->ReportCopy(destination, TypeDir,
643 !cmSystemTools::FileIsDirectory(destination));
644
645 // check if default dir creation permissions were set
646 mode_t default_dir_mode_v = 0;
647 mode_t* default_dir_mode = &default_dir_mode_v;
648 if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
649 return false;
650 }
651
652 // Make sure the destination directory exists.
653 if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
654 std::ostringstream e;
655 e << this->Name << " cannot make directory \"" << destination
656 << "\": " << cmSystemTools::GetLastSystemError() << ".";
657 this->Status.SetError(e.str());
658 return false;
659 }
660
661 // Compute the requested permissions for the destination directory.
662 mode_t permissions =
663 (match_properties.Permissions ? match_properties.Permissions
664 : this->DirPermissions);
665 if (!permissions) {
666 // No permissions were explicitly provided but the user requested
667 // that the source directory permissions be used.
668 cmSystemTools::GetPermissions(source, permissions);
669 }
670
671 // Compute the set of permissions required on this directory to
672 // recursively install files and subdirectories safely.
673 mode_t required_permissions =
674 mode_owner_read | mode_owner_write | mode_owner_execute;
675
676 // If the required permissions are specified it is safe to set the
677 // final permissions now. Otherwise we must add the required
678 // permissions temporarily during file installation.
679 mode_t permissions_before = 0;
680 mode_t permissions_after = 0;
681 if ((permissions & required_permissions) == required_permissions) {
682 permissions_before = permissions;
683 } else {
684 permissions_before = permissions | required_permissions;
685 permissions_after = permissions;
686 }
687
688 // Set the required permissions of the destination directory.
689 if (!this->SetPermissions(destination, permissions_before)) {
690 return false;
691 }
692
693 // Load the directory contents to traverse it recursively.
694 cmsys::Directory dir;
695 if (!source.empty()) {
696 dir.Load(source);
697 }
698 unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles());
699 for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) {
700 if (!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
701 strcmp(dir.GetFile(fileNum), "..") == 0)) {
702 std::string fromPath = cmStrCat(source, '/', dir.GetFile(fileNum));
703 std::string toPath = cmStrCat(destination, '/', dir.GetFile(fileNum));
704 if (!this->Install(fromPath, toPath)) {
705 return false;
706 }
707 }
708 }
709
710 // Set the requested permissions of the destination directory.
711 return this->SetPermissions(destination, permissions_after);
712 }
713