File: file_monitor_impl.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 (195 lines) | stat: -rw-r--r-- 7,093 bytes parent folder | download | duplicates (11)
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/download/internal/background_service/file_monitor_impl.h"

#include "base/debug/alias.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/stl_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_CHROMEOS)
#include <grp.h>
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace download {

namespace {

// Creates the download directory if it doesn't exist.
bool InitializeAndCreateDownloadDirectory(const base::FilePath& dir_path) {
  // Create the download directory.
  bool success = base::PathExists(dir_path);
  if (!success) {
    base::File::Error error = base::File::Error::FILE_OK;
    success = base::CreateDirectoryAndGetError(dir_path, &error);
    if (!success)
      stats::LogsFileDirectoryCreationError(error);
  }
#if BUILDFLAG(IS_CHROMEOS)
  if (success) {
    // System daemons on ChromeOS may run as a user different than the Chrome
    // process but need to access files under the directory created here.
    // Because of that, grant the execute permission on the created directory
    // to group and other users. Also chronos-access group should have read
    // access to the directory.
    if (HANDLE_EINTR(chmod(dir_path.value().c_str(),
                           S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH)) != 0) {
      return false;
    }
    struct group grp, *result = nullptr;
    std::vector<char> buffer(16384);
    getgrnam_r("chronos-access", &grp, buffer.data(), buffer.size(), &result);
    // Ignoring as the group might not exist in tests.
    if (result) {
      success =
          HANDLE_EINTR(chown(dir_path.value().c_str(), -1, grp.gr_gid)) == 0;
    }
  }
#endif  // BUILDFLAG(IS_CHROMEOS)
  return success;
}

void GetFilesInDirectory(const base::FilePath& directory,
                         std::set<base::FilePath>& paths_out) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  base::FileEnumerator file_enumerator(directory, false /* recursive */,
                                       base::FileEnumerator::FILES);

  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    paths_out.insert(path);
  }
}

void DeleteFilesOnFileThread(const std::set<base::FilePath>& paths,
                             stats::FileCleanupReason reason) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  int num_delete_attempted = 0;
  int num_delete_failed = 0;
  int num_delete_by_external = 0;
  int num_files = paths.size();
  // Lock variables on the stack for investigating https://crbug.com/1428815
  base::debug::Alias(&num_files);
  base::debug::Alias(&num_delete_attempted);
  for (const base::FilePath& path : paths) {
    if (!base::PathExists(path)) {
      num_delete_by_external++;
      continue;
    }

    num_delete_attempted++;
    DCHECK(!base::DirectoryExists(path));

    if (!base::DeleteFile(path)) {
      num_delete_failed++;
    }
  }

  stats::LogFileCleanupStatus(reason, num_delete_attempted, num_delete_failed,
                              num_delete_by_external);
}

void DeleteUnknownFilesOnFileThread(
    const base::FilePath& directory,
    const std::set<base::FilePath>& download_file_paths) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  std::set<base::FilePath> files_in_dir;
  GetFilesInDirectory(directory, files_in_dir);

  std::set<base::FilePath> files_to_remove =
      base::STLSetDifference<std::set<base::FilePath>>(files_in_dir,
                                                       download_file_paths);
  DeleteFilesOnFileThread(files_to_remove, stats::FileCleanupReason::UNKNOWN);
}

bool HardRecoverOnFileThread(const base::FilePath& directory) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  std::set<base::FilePath> files_in_dir;
  GetFilesInDirectory(directory, files_in_dir);
  DeleteFilesOnFileThread(files_in_dir,
                          stats::FileCleanupReason::HARD_RECOVERY);
  return InitializeAndCreateDownloadDirectory(directory);
}

}  // namespace

FileMonitorImpl::FileMonitorImpl(
    const base::FilePath& download_file_dir,
    const scoped_refptr<base::SequencedTaskRunner>& file_thread_task_runner)
    : download_file_dir_(download_file_dir),
      file_thread_task_runner_(file_thread_task_runner) {}

FileMonitorImpl::~FileMonitorImpl() = default;

void FileMonitorImpl::Initialize(InitCallback callback) {
  file_thread_task_runner_->PostTaskAndReplyWithResult(
      FROM_HERE,
      base::BindOnce(&InitializeAndCreateDownloadDirectory, download_file_dir_),
      std::move(callback));
}

void FileMonitorImpl::DeleteUnknownFiles(
    const Model::EntryList& known_entries,
    const std::vector<DriverEntry>& known_driver_entries,
    base::OnceClosure completion_callback) {
  std::set<base::FilePath> download_file_paths;
  for (Entry* entry : known_entries) {
    download_file_paths.insert(entry->target_file_path);
  }

  for (const DriverEntry& driver_entry : known_driver_entries) {
    download_file_paths.insert(driver_entry.current_file_path);
  }

  file_thread_task_runner_->PostTaskAndReply(
      FROM_HERE,
      base::BindOnce(&DeleteUnknownFilesOnFileThread, download_file_dir_,
                     download_file_paths),
      std::move(completion_callback));
}

void FileMonitorImpl::CleanupFilesForCompletedEntries(
    const Model::EntryList& entries,
    base::OnceClosure completion_callback) {
  std::set<base::FilePath> files_to_remove;
  for (auto* entry : entries) {
    files_to_remove.insert(entry->target_file_path);

    // TODO(xingliu): Consider logs life time after the file being deleted on
    // the file thread.
    stats::LogFileLifeTime(base::Time::Now() - entry->completion_time);
  }

  file_thread_task_runner_->PostTaskAndReply(
      FROM_HERE,
      base::BindOnce(&DeleteFilesOnFileThread, files_to_remove,
                     stats::FileCleanupReason::TIMEOUT),
      std::move(completion_callback));
}

void FileMonitorImpl::DeleteFiles(
    const std::set<base::FilePath>& files_to_remove,
    stats::FileCleanupReason reason) {
  file_thread_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&DeleteFilesOnFileThread, files_to_remove, reason));
}

void FileMonitorImpl::HardRecover(InitCallback callback) {
  file_thread_task_runner_->PostTaskAndReplyWithResult(
      FROM_HERE, base::BindOnce(&HardRecoverOnFileThread, download_file_dir_),
      std::move(callback));
}

}  // namespace download