Coverage Report

Created: 2026-03-12 06:35

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