| 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); |
| 23 |
| 24 for (base::FilePath current = traversal.Next(); !current.empty(); | 24 for (base::FilePath current = traversal.Next(); !current.empty(); |
| 25 current = traversal.Next()) { | 25 current = traversal.Next()) { |
| 26 // Try to clear the read-only bit if we find it. | 26 // Try to clear the read-only bit if we find it. |
| 27 base::FileEnumerator::FileInfo info = traversal.GetInfo(); | 27 base::FileEnumerator::FileInfo info = traversal.GetInfo(); |
| 28 if (info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) { | 28 if (info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) { |
| 29 SetFileAttributes( | 29 SetFileAttributes( |
| 30 current.value().c_str(), | 30 current.value().c_str(), |
| 31 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); | 31 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); |
| 32 } | 32 } |
| 33 | 33 |
| 34 if (info.IsDirectory()) { | 34 // JumpListIcons* directories shouldn't have sub-directories. If any of them |
| 35 // JumpListIcons{,Old} directories shouldn't have sub-directories. | 35 // does for unknown reasons, don't delete them. Instead, increment the |
| 36 // If any of them does for unknown reasons, don't delete them. Instead, | 36 // failure count. |
| 37 // increment the failure count and record this information. | 37 if (info.IsDirectory() || !::DeleteFile(current.value().c_str())) |
| 38 delete_status = FAIL_SUBDIRECTORY_EXISTS; | |
| 39 failure_count++; | 38 failure_count++; |
| 40 } else if (!::DeleteFile(current.value().c_str())) { | 39 else |
| 41 failure_count++; | |
| 42 } else { | |
| 43 success_count++; | 40 success_count++; |
| 44 } | 41 |
| 45 // If it deletes max_file_deleted files with any attempt failures, record | 42 // The desired max number of files have been deleted, or the desired max |
| 46 // this information in |delete_status|. | 43 // number of failures have been hit. |
| 47 if (success_count >= max_file_deleted) { | 44 if (success_count >= max_file_deleted || failure_count >= max_file_deleted) |
| 48 // The desired max number of files have been deleted. | 45 break; |
| 49 return failure_count ? FAIL_DELETE_MAX_FILES_WITH_ERRORS : delete_status; | |
| 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* are |
| 83 // are directories, hitting the code inside the if-block below is unexpected. | 67 // 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 ::RemoveDirectory(path.value().c_str()); |
| 100 // 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 | |
| 102 // if it is. | |
| 103 if (base::IsDirectoryEmpty(path) && | |
| 104 !::RemoveDirectory(path.value().c_str())) { | |
| 105 delete_status = FAIL_REMOVE_RAW_DIRECTORY; | |
| 106 } | |
| 107 return delete_status; | |
| 108 } | 84 } |
| 109 | 85 |
| 110 void DeleteDirectoryAndLogResults(const base::FilePath& path, | 86 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) { | 87 int max_file_deleted) { |
| 128 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | |
| 129 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.DeleteDirectoryContentDuration"); | 88 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.DeleteDirectoryContentDuration"); |
| 130 | 89 |
| 131 DirectoryStatus dir_status = NON_EXIST; | 90 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 } | 91 } |
| OLD | NEW |