Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Side by Side Diff: chrome/installer/util/delete_old_versions.cc

Issue 1666363002: Delete old files after an update. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: self-review Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/installer/util/delete_old_versions.h"
6
7 #include <map>
8 #include <set>
9 #include <vector>
10
11 #include "base/file_version_info.h"
12 #include "base/files/file.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/version.h"
22 #include "chrome/installer/util/callback_work_item.h"
23 #include "chrome/installer/util/util_constants.h"
24
25 namespace {
26
27 using PathVector = std::vector<base::FilePath>;
28
29 // Returns the name of the version directory for executable |exe_path|.
30 base::FilePath GetExecutableVersionDirName(const base::FilePath& exe_path) {
31 scoped_ptr<FileVersionInfo> file_version_info(
32 FileVersionInfo::CreateFileVersionInfo(exe_path));
33 if (!file_version_info.get())
34 return base::FilePath();
35 return base::FilePath(file_version_info->file_version());
36 }
37
38 // Returns the names of the version directories found in |install_dir|. The
39 // directories named after the version of chrome.exe or new_chrome.exe are
40 // excluded.
41 using DirectorySet = std::set<base::FilePath>;
grt (UTC plus 2) 2016/02/24 20:06:17 nit: move all of these aliases up to the top of th
fdoray 2016/02/25 18:26:47 Done.
42 DirectorySet GetDirectories(const base::FilePath& install_dir) {
grt (UTC plus 2) 2016/02/24 20:06:16 nit: GetVersionDirectories since it is careful not
fdoray 2016/02/25 18:26:47 Done.
43 const base::FilePath new_chrome_exe_version_dir_name =
44 GetExecutableVersionDirName(install_dir.Append(installer::kChromeNewExe));
45 const base::FilePath chrome_exe_version_dir_name =
46 GetExecutableVersionDirName(install_dir.Append(installer::kChromeExe));
47
48 DirectorySet directories;
49 base::FileEnumerator enum_directories(install_dir, false,
50 base::FileEnumerator::DIRECTORIES);
51 for (base::FilePath directory_path = enum_directories.Next();
52 !directory_path.empty(); directory_path = enum_directories.Next()) {
53 const base::FilePath directory_name = directory_path.BaseName();
54 const base::Version version(base::UTF16ToUTF8(directory_name.value()));
grt (UTC plus 2) 2016/02/24 20:06:16 base::UTF16ToUTF8(directory_name.value()) -> direc
fdoray 2016/02/25 18:26:47 Done.
55 const size_t kNumChromeVersionComponents = 4;
56 if (directory_name != new_chrome_exe_version_dir_name &&
grt (UTC plus 2) 2016/02/24 20:06:16 nit: version.IsValid() and the size check are both
fdoray 2016/02/25 18:26:47 Done.
57 directory_name != chrome_exe_version_dir_name && version.IsValid() &&
58 version.components().size() == kNumChromeVersionComponents) {
59 directories.insert(directory_name);
60 }
61 }
62 return directories;
63 }
64
65 // Returns a map where the keys are version directory names and values are paths
66 // of old_chrome*.exe executables found in |install_dir|.
67 using ExecutableMap = std::map<base::FilePath, PathVector>;
68 ExecutableMap GetExecutables(const base::FilePath& install_dir) {
69 ExecutableMap executables;
70 base::FileEnumerator enum_executables(install_dir, false,
71 base::FileEnumerator::FILES,
72 FILE_PATH_LITERAL("old_chrome*.exe"));
73 for (base::FilePath exe_path = enum_executables.Next(); !exe_path.empty();
74 exe_path = enum_executables.Next()) {
75 executables[GetExecutableVersionDirName(exe_path)].push_back(exe_path);
76 }
77 return executables;
78 }
79
80 // Deletes directories that are in |directories| and don't have a matching
81 // executable in |executables|.
grt (UTC plus 2) 2016/02/24 20:06:17 "Returns false if any such directories could not b
fdoray 2016/02/25 18:26:47 Done.
82 bool DeleteDirectoriesWithoutMatchingExecutable(
83 const DirectorySet& directories,
84 const ExecutableMap& executables,
85 const base::FilePath& install_dir) {
86 bool success = true;
87 for (const base::FilePath& directory_name : directories) {
88 // Delete the directory if it doesn't have a matching executable.
89 if (!ContainsKey(executables, directory_name)) {
90 LOG(WARNING) << "Attempting to delete stray directory "
91 << directory_name.value();
92 if (!base::DeleteFile(install_dir.Append(directory_name), true)) {
grt (UTC plus 2) 2016/02/24 20:06:16 nit: put install_dir.Append(directory_name) in a l
fdoray 2016/02/25 18:26:47 Done.
93 PLOG(ERROR) << "Failed to delete stray directory "
94 << directory_name.value();
95 success = false;
96 }
97 }
98 }
99 return success;
100 }
101
102 // Deletes executables that are in |executables| and don't have a matching
103 // directory in |directories|.
grt (UTC plus 2) 2016/02/24 20:06:17 "Returns false if any such files could not be dele
fdoray 2016/02/25 18:26:47 Done.
104 bool DeleteExecutablesWithoutMatchingDirectory(
105 const DirectorySet& directories,
106 const ExecutableMap& executables) {
107 bool success = true;
108 for (const auto& version_and_executables : executables) {
109 const auto& version_dir_name = version_and_executables.first;
110 const auto& executables_for_version = version_and_executables.second;
111
112 // Don't delete the executables if they have a matching directory.
113 if (ContainsValue(directories, version_dir_name))
114 continue;
115
116 // Delete executables for version |version|.
grt (UTC plus 2) 2016/02/24 20:06:17 version_dir_name
fdoray 2016/02/25 18:26:47 Done.
117 for (const auto& executable_path : executables_for_version) {
118 const base::FilePath executable_name = executable_path.BaseName();
grt (UTC plus 2) 2016/02/24 20:06:17 nit: use executable_path in the log messages
fdoray 2016/02/25 18:26:47 Done.
119 LOG(WARNING) << "Attempting to delete stray executable "
120 << executable_name.value();
121 if (!base::DeleteFile(executable_path, false)) {
122 PLOG(ERROR) << "Failed to delete stray executable "
123 << executable_name.value();
124 success = false;
125 }
126 }
127 }
128 return success;
129 }
130
131 // Opens |path| with options that prevent the file from being read or written
132 // via another handle. As long as the returned object is alive, it is guaranteed
133 // that |path| isn't in use. It can however be deleted.
134 base::File GetFileLock(const base::FilePath path) {
grt (UTC plus 2) 2016/02/24 20:06:17 const base::FilePath&
fdoray 2016/02/25 18:26:47 Done.
135 return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
136 base::File::FLAG_EXCLUSIVE_READ |
137 base::File::FLAG_EXCLUSIVE_WRITE |
138 base::File::FLAG_SHARE_DELETE);
139 }
140
141 // Deletes |version_directory| and all executables in |version_executables| if
142 // no .exe or .dll file for the version is in use.
143 bool DeleteVersion(const base::FilePath& version_directory,
144 const PathVector& version_executables) {
145 std::vector<base::File> locks;
146 PathVector locked_file_paths;
147
148 // Lock .exe/.dll files in |version_directory|.
149 base::FileEnumerator enum_version_directory(version_directory, true,
150 base::FileEnumerator::FILES);
151 for (base::FilePath path = enum_version_directory.Next(); !path.empty();
152 path = enum_version_directory.Next()) {
153 if (!path.MatchesExtension(FILE_PATH_LITERAL(".exe")) &&
154 !path.MatchesExtension(FILE_PATH_LITERAL(".dll"))) {
155 continue;
156 }
157 locks.push_back(GetFileLock(path));
158 if (!locks.back().IsValid()) {
159 LOG(WARNING) << "Failed to delete old version "
160 << version_directory.value() << " because " << path.value()
161 << " is in use.";
162 return false;
163 }
164 locked_file_paths.push_back(path);
165 }
166
167 // Lock executables in |version_executables|.
168 for (const base::FilePath& executable_path : version_executables) {
169 locks.push_back(GetFileLock(executable_path));
170 if (!locks.back().IsValid()) {
171 LOG(WARNING) << "Failed to delete old version "
172 << version_directory.value() << " because "
173 << executable_path.value() << " is in use.";
174 return false;
175 }
176 locked_file_paths.push_back(executable_path);
177 }
178
179 bool success = true;
180
181 // Delete locked files. The files won't actually be deleted until the locks
182 // are released.
183 for (const base::FilePath& locked_file_path : locked_file_paths) {
184 if (!base::DeleteFile(locked_file_path, false)) {
185 PLOG(ERROR) << "Failed to delete locked file "
186 << locked_file_path.value();
187 success = false;
188 }
189 }
190
191 // Release the locks, causing the locked files to actually be deleted. The
192 // version directory can't be deleted before this is done.
193 locks.clear();
194
195 // Delete the version directory.
196 if (!base::DeleteFile(version_directory, true)) {
197 PLOG(ERROR) << "Failed to delete version directory "
198 << version_directory.value();
199 success = false;
200 }
201
202 return success;
203 }
204
205 // For each executable in |executables| that has a matching directory in
206 // |directories|, tries to delete the executable and the matching directory. No
207 // deletion occurs for a given version if a .exe or .dll file for that version
208 // is in use.
209 bool DeleteMatchingExecutablesAndDirectories(
210 const DirectorySet& directories,
211 const ExecutableMap& executables,
212 const base::FilePath& install_dir) {
213 bool success = true;
214 for (const auto& directory_name : directories) {
215 ExecutableMap::const_iterator version_executables_it =
grt (UTC plus 2) 2016/02/24 20:06:17 auto?
fdoray 2016/02/25 18:26:47 Done.
216 executables.find(directory_name);
217
218 // Don't delete the version unless the directory has at least one matching
grt (UTC plus 2) 2016/02/24 20:06:17 nit: move this comment above the call to find()
fdoray 2016/02/25 18:26:47 Done.
219 // executable.
220 if (version_executables_it == executables.end())
221 continue;
222
223 // Try to delete all files for the version.
224 success &= DeleteVersion(install_dir.Append(directory_name),
225 version_executables_it->second);
226 }
227 return success;
228 }
229
230 } // namespace
231
232 bool DeleteOldVersions(const base::FilePath& install_dir) {
233 const DirectorySet directories = GetDirectories(install_dir);
234 const ExecutableMap executables = GetExecutables(install_dir);
235
236 bool success = true;
237 success &= DeleteDirectoriesWithoutMatchingExecutable(
238 directories, executables, install_dir);
239 success &=
240 DeleteExecutablesWithoutMatchingDirectory(directories, executables);
241 success &= DeleteMatchingExecutablesAndDirectories(directories, executables,
242 install_dir);
243
244 return success;
245 }
246
247 bool DeleteOldVersionsCallback(const base::FilePath& install_dir,
248 const CallbackWorkItem& work_item) {
249 // No rollback is possible for this action.
250 if (work_item.IsRollback())
251 return true;
252
253 return DeleteOldVersions(install_dir);
254 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698