cmFileCopier.cxx 21 KB

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