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