OLD | NEW |
---|---|
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/win/jumplist_file_util.h" | 5 #include "chrome/browser/win/jumplist_file_util.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/files/file_enumerator.h" | 9 #include "base/files/file_enumerator.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
12 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
13 | 13 |
14 FolderDeleteResult DeleteFiles(const base::FilePath& path, | 14 void DeleteFiles(const base::FilePath& path, |
15 const base::FilePath::StringType& pattern, | 15 const base::FilePath::StringType& pattern, |
16 int max_file_deleted) { | 16 int max_file_deleted) { |
17 int success_count = 0; | 17 int success_count = 0; |
18 int failure_count = 0; | 18 int failure_count = 0; |
19 FolderDeleteResult delete_status = SUCCEED; | |
20 | 19 |
21 base::FileEnumerator traversal( | 20 base::FileEnumerator traversal( |
22 path, false, | 21 path, false, |
23 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern); | 22 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern); |
24 for (base::FilePath current = traversal.Next(); !current.empty(); | 23 for (base::FilePath current = traversal.Next(); !current.empty(); |
25 current = traversal.Next()) { | 24 current = traversal.Next()) { |
26 // Try to clear the read-only bit if we find it. | 25 // Try to clear the read-only bit if we find it. |
27 base::FileEnumerator::FileInfo info = traversal.GetInfo(); | 26 base::FileEnumerator::FileInfo info = traversal.GetInfo(); |
28 if (info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) { | 27 if (info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) { |
29 SetFileAttributes( | 28 SetFileAttributes( |
30 current.value().c_str(), | 29 current.value().c_str(), |
31 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); | 30 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); |
32 } | 31 } |
33 | 32 |
34 if (info.IsDirectory()) { | 33 // JumpListIcons{,Old} directories shouldn't have sub-directories. |
35 // JumpListIcons{,Old} directories shouldn't have sub-directories. | 34 // If any of them does for unknown reasons, don't delete them. Instead, |
36 // If any of them does for unknown reasons, don't delete them. Instead, | 35 // increment the failure count. |
37 // increment the failure count and record this information. | 36 if (info.IsDirectory() || !::DeleteFile(current.value().c_str())) { |
grt (UTC plus 2)
2017/04/28 07:37:08
nit: omit braces
chengx
2017/04/28 22:29:29
Done. However, I think braces are allowed here.
h
grt (UTC plus 2)
2017/05/01 09:45:36
you're correct. one of the more important goals st
chengx
2017/05/01 18:04:30
Cool. Thanks for this reminder!
| |
38 delete_status = FAIL_SUBDIRECTORY_EXISTS; | |
39 failure_count++; | |
40 } else if (!::DeleteFile(current.value().c_str())) { | |
41 failure_count++; | 37 failure_count++; |
42 } else { | 38 } else { |
43 success_count++; | 39 success_count++; |
44 } | 40 } |
45 // If it deletes max_file_deleted files with any attempt failures, record | 41 |
46 // this information in |delete_status|. | 42 // The desired max number of files have been deleted, or The desired max |
47 if (success_count >= max_file_deleted) { | 43 // number of failures have been hit. |
48 // The desired max number of files have been deleted. | 44 if (success_count >= max_file_deleted || failure_count >= max_file_deleted) |
49 return failure_count ? FAIL_DELETE_MAX_FILES_WITH_ERRORS : delete_status; | 45 return; |
grt (UTC plus 2)
2017/04/28 07:37:08
nit: i slightly prefer breaking out of the loop he
chengx
2017/04/28 22:29:29
Done.
| |
50 } | |
51 if (failure_count >= max_file_deleted) { | |
52 // The desired max number of failures have been hit. | |
53 return FAIL_MAX_DELETE_FAILURES; | |
54 } | |
55 } | 46 } |
56 return delete_status; | |
57 } | 47 } |
58 | 48 |
59 FolderDeleteResult DeleteDirectoryContent(const base::FilePath& path, | 49 void DeleteDirectoryContent(const base::FilePath& path, int max_file_deleted) { |
60 int max_file_deleted) { | |
61 base::ThreadRestrictions::AssertIOAllowed(); | 50 base::ThreadRestrictions::AssertIOAllowed(); |
62 | 51 |
63 if (path.empty()) | 52 if (path.empty() || path.value().length() >= MAX_PATH) |
64 return SUCCEED; | 53 return; |
65 | |
66 // For JumpListIcons{,Old} directories, since their names are shorter than | |
67 // MAX_PATH, hitting the code in the if-block below is unexpected. | |
68 if (path.value().length() >= MAX_PATH) | |
69 return FAIL_INVALID_FILE_PATH; | |
70 | 54 |
71 DWORD attr = GetFileAttributes(path.value().c_str()); | 55 DWORD attr = GetFileAttributes(path.value().c_str()); |
72 // We're done if we can't find the path. | 56 // We're done if we can't find the path. |
73 if (attr == INVALID_FILE_ATTRIBUTES) | 57 if (attr == INVALID_FILE_ATTRIBUTES) |
74 return SUCCEED; | 58 return; |
75 // Try to clear the read-only bit if we find it. | 59 // Try to clear the read-only bit if we find it. |
76 if ((attr & FILE_ATTRIBUTE_READONLY) && | 60 if ((attr & FILE_ATTRIBUTE_READONLY) && |
77 !SetFileAttributes(path.value().c_str(), | 61 !SetFileAttributes(path.value().c_str(), |
78 attr & ~FILE_ATTRIBUTE_READONLY)) { | 62 attr & ~FILE_ATTRIBUTE_READONLY)) { |
79 return FAIL_READ_ONLY_DIRECTORY; | 63 return; |
80 } | 64 } |
81 | 65 |
82 // If |path| is a file, simply delete it. However, since JumpListIcons{,Old} | 66 // If |path| is a file, simply delete it. However, since JumpListIcons{,Old} |
83 // are directories, hitting the code inside the if-block below is unexpected. | 67 // are directories, hitting the code inside the if-block below is unexpected. |
84 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { | 68 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { |
85 ::DeleteFile(path.value().c_str()); | 69 ::DeleteFile(path.value().c_str()); |
86 return FAIL_DELETE_SINGLE_FILE; | 70 return; |
87 } | 71 } |
88 | 72 |
89 // If |path| is a directory, delete at most |max_file_deleted| files in it. | 73 // If |path| is a directory, delete at most |max_file_deleted| files in it. |
90 return DeleteFiles(path, L"*", max_file_deleted); | 74 DeleteFiles(path, L"*", max_file_deleted); |
91 } | 75 } |
92 | 76 |
93 FolderDeleteResult DeleteDirectory(const base::FilePath& path, | 77 void DeleteDirectory(const base::FilePath& path, int max_file_deleted) { |
94 int max_file_deleted) { | |
95 base::ThreadRestrictions::AssertIOAllowed(); | 78 base::ThreadRestrictions::AssertIOAllowed(); |
79 | |
96 // Delete at most |max_file_deleted| files in |path|. | 80 // Delete at most |max_file_deleted| files in |path|. |
97 FolderDeleteResult delete_status = | 81 DeleteDirectoryContent(path, max_file_deleted); |
98 DeleteDirectoryContent(path, max_file_deleted); | 82 |
99 // Since DeleteDirectoryContent() can only delete at most |max_file_deleted| | 83 // Since DeleteDirectoryContent() can only delete at most |max_file_deleted| |
100 // files, its return value cannot indicate if |path| is empty or not. | 84 // files, its return value cannot indicate if |path| is empty or not. |
101 // Instead, use PathIsDirectoryEmpty to check if |path| is empty and remove it | 85 // Instead, use IsDirectoryEmpty to check if |path| is empty and remove it |
102 // if it is. | 86 // if it is. |
103 if (base::IsDirectoryEmpty(path) && | 87 if (base::IsDirectoryEmpty(path)) |
grt (UTC plus 2)
2017/04/28 07:37:08
if you no longer care whether RemoveDirectory succ
chengx
2017/04/28 22:29:29
Done.
| |
104 !::RemoveDirectory(path.value().c_str())) { | 88 ::RemoveDirectory(path.value().c_str()); |
105 delete_status = FAIL_REMOVE_RAW_DIRECTORY; | |
106 } | |
107 return delete_status; | |
108 } | 89 } |
109 | 90 |
110 void DeleteDirectoryAndLogResults(const base::FilePath& path, | 91 void DeleteDirectoryContentAndLogRuntime(const base::FilePath& path, |
111 int max_file_deleted) { | |
112 DirectoryStatus dir_status = NON_EXIST; | |
113 | |
114 if (base::DirectoryExists(path)) { | |
115 FolderDeleteResult delete_status = DeleteDirectory(path, max_file_deleted); | |
116 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DeleteStatusJumpListIconsOld", | |
117 delete_status, FolderDeleteResult::END); | |
118 if (base::DirectoryExists(path)) | |
119 dir_status = base::IsDirectoryEmpty(path) ? EMPTY : NON_EMPTY; | |
120 } | |
121 | |
122 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DirectoryStatusJumpListIconsOld", | |
123 dir_status, DIRECTORY_STATUS_END); | |
124 } | |
125 | |
126 void DeleteDirectoryContentAndLogResults(const base::FilePath& path, | |
127 int max_file_deleted) { | 92 int max_file_deleted) { |
128 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | |
129 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.DeleteDirectoryContentDuration"); | 93 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.DeleteDirectoryContentDuration"); |
130 | 94 |
131 DirectoryStatus dir_status = NON_EXIST; | 95 DeleteDirectoryContent(path, kFileDeleteLimit); |
132 | |
133 // Delete the content in |path|. If |path| doesn't exist, create one. | |
134 if (base::DirectoryExists(path)) { | |
135 FolderDeleteResult delete_status = | |
136 DeleteDirectoryContent(path, kFileDeleteLimit); | |
137 | |
138 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DeleteStatusJumpListIcons", | |
139 delete_status, FolderDeleteResult::END); | |
140 | |
141 if (base::DirectoryExists(path)) | |
142 dir_status = base::IsDirectoryEmpty(path) ? EMPTY : NON_EMPTY; | |
143 } else if (base::CreateDirectory(path)) { | |
144 dir_status = EMPTY; | |
145 } | |
146 | |
147 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DirectoryStatusJumpListIcons", | |
148 dir_status, DIRECTORY_STATUS_END); | |
149 } | 96 } |
OLD | NEW |