File: downgrade_utils.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (106 lines) | stat: -rw-r--r-- 4,140 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/downgrade/downgrade_utils.h"

#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "chrome/browser/downgrade/user_data_downgrade.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>
#elif BUILDFLAG(IS_POSIX)
#include "base/files/file.h"
#endif

namespace downgrade {

base::FilePath GetTempDirNameForDelete(const base::FilePath& dir,
                                       const base::FilePath& name) {
  if (dir.empty())
    return base::FilePath();

  return base::GetUniquePath(
      dir.Append(name).AddExtension(kDowngradeDeleteSuffix));
}

bool MoveWithoutFallback(const base::FilePath& source,
                         const base::FilePath& target) {
#if BUILDFLAG(IS_WIN)
  // TODO(grt): check whether or not this is sufficiently atomic when |source|
  // is on a network share.
  auto result = ::MoveFileEx(source.value().c_str(), target.value().c_str(), 0);
  PLOG_IF(ERROR, !result) << source << " -> " << target;
  return result;
#elif BUILDFLAG(IS_POSIX)
  // Windows compatibility: if |target| exists, |source| and |target|
  // must be the same type, either both files, or both directories.
  base::stat_wrapper_t target_info;
  if (base::File::Stat(target, &target_info) == 0) {
    base::stat_wrapper_t source_info;
    if (base::File::Stat(source, &source_info) != 0) {
      return false;
    }
    if (S_ISDIR(target_info.st_mode) != S_ISDIR(source_info.st_mode))
      return false;
  }

  auto result = rename(source.value().c_str(), target.value().c_str());
  PLOG_IF(ERROR, result) << source << " -> " << target;
  return !result;
#endif
}

bool MoveContents(const base::FilePath& source,
                  const base::FilePath& target,
                  ExclusionPredicate exclusion_predicate) {
  // Implementation note: moving is better than deleting in this case since it
  // avoids certain failure modes. For example: on Windows, a file that is open
  // with FILE_SHARE_DELETE can be moved or marked for deletion. If it is moved
  // aside, the containing directory may then be eligible for deletion. If, on
  // the other hand, it is marked for deletion, it cannot be moved nor can its
  // containing directory be moved or deleted.
  bool all_succeeded = base::CreateDirectory(target);
  if (!all_succeeded) {
    PLOG(ERROR) << target;
    return all_succeeded;
  }

  base::FileEnumerator enumerator(
      source, false,
      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = enumerator.Next(); !path.empty();
       path = enumerator.Next()) {
    const base::FileEnumerator::FileInfo info = enumerator.GetInfo();
    const base::FilePath name = info.GetName();
    if (exclusion_predicate && exclusion_predicate.Run(name))
      continue;
    const base::FilePath this_target = target.Append(name);
    // A directory can be moved unless any file within it is open. A simple file
    // can be moved unless it is opened without FILE_SHARE_DELETE. (As with most
    // things in life, there are exceptions to this rule, but they are
    // uncommon. For example, a file opened without FILE_SHARE_DELETE can be
    // moved as long as it was opened only with some combination of
    // READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE access rights.
    // Since this short list excludes such useful rights as FILE_EXECUTE,
    // FILE_READ_DATA, and most anything else one would want a file for, it's
    // likely an uncommon scenario. See OpenFileTest in base/files for more.)
    if (MoveWithoutFallback(path, this_target))
      continue;
    if (!info.IsDirectory()) {
      all_succeeded = false;
      continue;
    }
    MoveContents(path, this_target, ExclusionPredicate());
    // If everything within the directory was moved, it may be possible to
    // delete it now.
    if (!base::DeleteFile(path))
      all_succeeded = false;
  }
  return all_succeeded;
}

}  // namespace downgrade