cmFileCopier.cxx 21 KB

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