Coverage Report

Created: 2026-02-09 06:05

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