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

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: 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 (c) 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/strings/string16.h"
19 #include "chrome/installer/util/util_constants.h"
20
21 namespace {
22
23 // Returns the version of the executable |exe_path|.
24 base::string16 GetExecutableVersion(const base::FilePath& exe_path) {
25 scoped_ptr<FileVersionInfo> file_version_info(
26 FileVersionInfo::CreateFileVersionInfo(exe_path));
27 if (!file_version_info.get())
28 return base::string16();
29 return file_version_info->file_version();
30 }
31
32 // Returns the directory names found in |install_dir|. The directories named
33 // after the version of chrome.exe or new_chrome.exe are excluded.
34 using DirectorySet = std::set<base::string16>;
35 DirectorySet GetDirectories(const base::FilePath& install_dir) {
36 const base::string16 new_chrome_exe_version =
37 GetExecutableVersion(install_dir.Append(installer::kChromeNewExe));
38 const base::string16 chrome_exe_version =
39 GetExecutableVersion(install_dir.Append(installer::kChromeExe));
40
41 DirectorySet directories;
42 base::FileEnumerator enum_directories(install_dir, false,
43 base::FileEnumerator::DIRECTORIES);
44 for (base::FilePath directory_path = enum_directories.Next();
45 !directory_path.empty(); directory_path = enum_directories.Next()) {
46 const base::string16 directory_name = directory_path.BaseName().value();
grt (UTC plus 2) 2016/02/10 20:02:04 nit: make this and the container use base::FilePat
fdoray 2016/02/18 17:59:20 Done.
47 if (directory_name != new_chrome_exe_version &&
48 directory_name != chrome_exe_version) {
49 directories.insert(directory_name);
grt (UTC plus 2) 2016/02/10 20:02:04 for the sake of caution, perhaps this should only
fdoray 2016/02/18 17:59:20 Done.
50 }
51 }
52 return directories;
53 }
54
55 // Returns a map where the keys are versions and values are paths of
56 // old_chrome*.exe executables found in |install_dir|.
57 using ExecutableVector = std::vector<base::FilePath>;
grt (UTC plus 2) 2016/02/10 20:02:04 nit: change this type to PathVector and use it on
fdoray 2016/02/18 17:59:20 Done.
58 using ExecutableMap = std::map<base::string16, ExecutableVector>;
grt (UTC plus 2) 2016/02/10 20:02:03 maybe this should also map a FilePath to a vector
fdoray 2016/02/18 17:59:20 Done.
59 ExecutableMap GetExecutables(const base::FilePath& install_dir) {
60 ExecutableMap executables;
61 base::FileEnumerator enum_executables(install_dir, false,
62 base::FileEnumerator::FILES,
63 FILE_PATH_LITERAL("old_chrome*.exe"));
64 for (base::FilePath exe_path = enum_executables.Next(); !exe_path.empty();
65 exe_path = enum_executables.Next()) {
66 executables[GetExecutableVersion(exe_path)].push_back(exe_path);
67 }
68 return executables;
69 }
70
71 // Deletes directories that are in |directories| and don't have a matching
72 // executable in |executables|.
73 bool DeleteDirectoriesWithoutMatchingExecutable(
74 const DirectorySet& directories,
75 const ExecutableMap& executables,
76 const base::FilePath& install_dir) {
77 bool success = true;
78 for (const base::string16& directory_name : directories) {
79 // Delete the directory if it doesn't have a matching executable.
80 if (executables.find(directory_name) == executables.end())
grt (UTC plus 2) 2016/02/10 20:02:04 nit: #include "base/stl_util.h" if (!ContainsV
fdoray 2016/02/18 17:59:20 Done.
81 success &= base::DeleteFile(install_dir.Append(directory_name), true);
grt (UTC plus 2) 2016/02/10 20:02:03 we expect that this will never happen, so i think
fdoray 2016/02/18 17:59:20 Done.
82 }
83 return success;
84 }
85
86 // Deletes executables that are in |executables| and don't have a matching
87 // directory in |directories|.
88 bool DeleteExecutablesWithoutMatchingDirectory(
89 const DirectorySet& directories,
90 const ExecutableMap& executables) {
91 bool success = true;
92 for (const auto& version_and_executables : executables) {
93 const auto& version = version_and_executables.first;
94 const auto& executables_for_version = version_and_executables.second;
95
96 // Don't delete the executables if they have a matching directory.
97 if (directories.find(version) != directories.end())
grt (UTC plus 2) 2016/02/10 20:02:03 if (ContainsValue(directories, version))
fdoray 2016/02/18 17:59:20 Done.
98 continue;
99
100 // Delete executables for version |version|.
101 for (const auto& executable_path : executables_for_version)
102 success &= base::DeleteFile(executable_path, false);
grt (UTC plus 2) 2016/02/10 20:02:04 we expect that this will never happen, so i think
fdoray 2016/02/18 17:59:20 Done.
103 }
104 return success;
105 }
106
107 // Opens |path| with options that prevent the file from being read or written
108 // via another handle. As long as the returned object is alive, it is guaranteed
109 // that |path| isn't in use. It can however be deleted.
110 base::File GetFileLock(const base::FilePath path) {
111 return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
112 base::File::FLAG_EXCLUSIVE_READ |
113 base::File::FLAG_EXCLUSIVE_WRITE |
114 base::File::FLAG_SHARE_DELETE);
115 }
116
117 // Deletes |version_directory| and all executables in |version_executables| if
118 // no .exe or .dll file for the version is in use.
119 bool DeleteVersion(const base::FilePath& version_directory,
120 const ExecutableVector& version_executables) {
121 std::vector<base::File> locks;
122 std::vector<base::FilePath> locked_file_paths;
123
124 // Lock .exe/.dll files in |version_directory|.
125 base::FileEnumerator enum_version_directory(version_directory, true,
126 base::FileEnumerator::FILES);
127 for (base::FilePath path = enum_version_directory.Next(); !path.empty();
128 path = enum_version_directory.Next()) {
129 if (path.MatchesExtension(L".exe") || path.MatchesExtension(L".dll")) {
grt (UTC plus 2) 2016/02/10 20:02:04 nit: use FILE_PATH_LITERAL("") rather than L"" for
grt (UTC plus 2) 2016/02/10 20:02:04 nit: reverse the logic here to "continue" when not
fdoray 2016/02/18 17:59:20 Done.
fdoray 2016/02/18 17:59:20 Done.
130 base::File lock(GetFileLock(path));
grt (UTC plus 2) 2016/02/10 20:02:04 wdyt of doing away with the local here and on line
fdoray 2016/02/18 17:59:20 Done.
131 if (!lock.IsValid())
132 return false;
grt (UTC plus 2) 2016/02/10 20:02:03 It makes sense to use LOG(WARNING) to indicate wha
fdoray 2016/02/18 17:59:20 Done.
133 locks.push_back(std::move(lock));
grt (UTC plus 2) 2016/02/10 20:02:03 #include <utility> if you keep this
fdoray 2016/02/18 17:59:20 I no longer use std::move.
134 locked_file_paths.push_back(path);
135 }
136 }
137
138 // Lock executables in |version_executables|.
139 for (const base::FilePath& executable_path : version_executables) {
140 base::File lock(GetFileLock(executable_path));
141 if (!lock.IsValid())
142 return false;
grt (UTC plus 2) 2016/02/10 20:02:03 same comment here about logging
fdoray 2016/02/18 17:59:20 Done.
143 locks.push_back(std::move(lock));
144 locked_file_paths.push_back(executable_path);
145 }
146
147 bool success = true;
148
149 // Delete locked files. The files won't actually be deleted until the locks
150 // are released.
151 for (const base::FilePath& locked_file_path : locked_file_paths)
152 success &= base::DeleteFile(locked_file_path, false);
grt (UTC plus 2) 2016/02/10 20:02:04 this isn't ever expected to fail, right? PLOG(ERRO
fdoray 2016/02/18 17:59:20 Done.
153
154 // Release the locks, causing the locked files to actually be deleted. The
155 // version directory can't be deleted before this is done.
156 locks.clear();
157
158 // Delete the version directory.
159 success &= base::DeleteFile(version_directory, true);
grt (UTC plus 2) 2016/02/10 20:02:04 PLOG(ERROR) when this fails so we understand why
fdoray 2016/02/18 17:59:20 Done.
160
161 return success;
162 }
163
164 // For each executable in |executables| that has a matching directory in
165 // |directories|, tries to delete the executable and the matching directory. No
166 // deletion occurs for a given version if a .exe or .dll file for that version
167 // is in use.
168 bool DeleteMatchingExecutablesAndDirectories(
169 const DirectorySet& directories,
170 const ExecutableMap& executables,
171 const base::FilePath& install_dir) {
172 bool success = true;
173 for (const auto directory_name : directories) {
grt (UTC plus 2) 2016/02/10 20:02:04 nit: const auto&
fdoray 2016/02/18 17:59:20 Done.
174 ExecutableMap::const_iterator version_executables_it =
175 executables.find(directory_name);
176
177 // Check if the directory has at least one matching executable.
178 if (version_executables_it == executables.end())
179 continue;
180
181 // Try to delete all files for the version.
182 success &= DeleteVersion(install_dir.Append(directory_name),
183 version_executables_it->second);
184 }
185 return success;
186 }
187
188 } // namespace
189
190 bool DeleteOldVersions(const base::FilePath& install_dir) {
191 const DirectorySet directories = GetDirectories(install_dir);
192 const ExecutableMap executables = GetExecutables(install_dir);
193
194 bool success = true;
195 success &= DeleteDirectoriesWithoutMatchingExecutable(
grt (UTC plus 2) 2016/02/10 20:02:04 I'm not a fan of using bitwise operations on bools
fdoray 2016/02/18 17:59:20 I want the function to return a bool so that we kn
196 directories, executables, install_dir);
197 success &=
198 DeleteExecutablesWithoutMatchingDirectory(directories, executables);
199 success &= DeleteMatchingExecutablesAndDirectories(directories, executables,
200 install_dir);
201
202 return success;
203 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698