cmFileCopier.cxx 21 KB

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