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

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

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

Powered by Google App Engine
This is Rietveld 408576698