| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "base/file_util.h" | 5 #include "base/file_util.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <psapi.h> | 8 #include <psapi.h> |
| 9 #include <shellapi.h> | 9 #include <shellapi.h> |
| 10 #include <shlobj.h> | 10 #include <shlobj.h> |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 #include "base/win/scoped_handle.h" | 27 #include "base/win/scoped_handle.h" |
| 28 #include "base/win/windows_version.h" | 28 #include "base/win/windows_version.h" |
| 29 | 29 |
| 30 namespace base { | 30 namespace base { |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 const DWORD kFileShareAll = | 34 const DWORD kFileShareAll = |
| 35 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; | 35 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; |
| 36 | 36 |
| 37 bool ShellCopy(const FilePath& from_path, const FilePath& to_path, |
| 38 bool recursive) { |
| 39 // WinXP SHFileOperation doesn't like trailing separators. |
| 40 FilePath stripped_from = from_path.StripTrailingSeparators(); |
| 41 FilePath stripped_to = to_path.StripTrailingSeparators(); |
| 42 |
| 43 ThreadRestrictions::AssertIOAllowed(); |
| 44 |
| 45 // NOTE: I suspect we could support longer paths, but that would involve |
| 46 // analyzing all our usage of files. |
| 47 if (stripped_from.value().length() >= MAX_PATH || |
| 48 stripped_to.value().length() >= MAX_PATH) { |
| 49 return false; |
| 50 } |
| 51 |
| 52 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, |
| 53 // so we have to use wcscpy because wcscpy_s writes non-NULLs |
| 54 // into the rest of the buffer. |
| 55 wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; |
| 56 wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; |
| 57 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation |
| 58 wcscpy(double_terminated_path_from, stripped_from.value().c_str()); |
| 59 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation |
| 60 wcscpy(double_terminated_path_to, stripped_to.value().c_str()); |
| 61 |
| 62 SHFILEOPSTRUCT file_operation = {0}; |
| 63 file_operation.wFunc = FO_COPY; |
| 64 file_operation.pFrom = double_terminated_path_from; |
| 65 file_operation.pTo = double_terminated_path_to; |
| 66 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | |
| 67 FOF_NOCONFIRMMKDIR; |
| 68 if (!recursive) |
| 69 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; |
| 70 |
| 71 return (SHFileOperation(&file_operation) == 0); |
| 72 } |
| 73 |
| 37 } // namespace | 74 } // namespace |
| 38 | 75 |
| 39 FilePath MakeAbsoluteFilePath(const FilePath& input) { | 76 FilePath MakeAbsoluteFilePath(const FilePath& input) { |
| 40 ThreadRestrictions::AssertIOAllowed(); | 77 ThreadRestrictions::AssertIOAllowed(); |
| 41 wchar_t file_path[MAX_PATH]; | 78 wchar_t file_path[MAX_PATH]; |
| 42 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) | 79 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) |
| 43 return FilePath(); | 80 return FilePath(); |
| 44 return FilePath(file_path); | 81 return FilePath(file_path); |
| 45 } | 82 } |
| 46 | 83 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 ThreadRestrictions::AssertIOAllowed(); | 141 ThreadRestrictions::AssertIOAllowed(); |
| 105 | 142 |
| 106 if (path.value().length() >= MAX_PATH) | 143 if (path.value().length() >= MAX_PATH) |
| 107 return false; | 144 return false; |
| 108 | 145 |
| 109 return MoveFileEx(path.value().c_str(), NULL, | 146 return MoveFileEx(path.value().c_str(), NULL, |
| 110 MOVEFILE_DELAY_UNTIL_REBOOT | | 147 MOVEFILE_DELAY_UNTIL_REBOOT | |
| 111 MOVEFILE_REPLACE_EXISTING) != FALSE; | 148 MOVEFILE_REPLACE_EXISTING) != FALSE; |
| 112 } | 149 } |
| 113 | 150 |
| 114 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { | |
| 115 ThreadRestrictions::AssertIOAllowed(); | |
| 116 | |
| 117 // NOTE: I suspect we could support longer paths, but that would involve | |
| 118 // analyzing all our usage of files. | |
| 119 if (from_path.value().length() >= MAX_PATH || | |
| 120 to_path.value().length() >= MAX_PATH) { | |
| 121 return false; | |
| 122 } | |
| 123 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), | |
| 124 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) | |
| 125 return true; | |
| 126 | |
| 127 // Keep the last error value from MoveFileEx around in case the below | |
| 128 // fails. | |
| 129 bool ret = false; | |
| 130 DWORD last_error = ::GetLastError(); | |
| 131 | |
| 132 if (DirectoryExists(from_path)) { | |
| 133 // MoveFileEx fails if moving directory across volumes. We will simulate | |
| 134 // the move by using Copy and Delete. Ideally we could check whether | |
| 135 // from_path and to_path are indeed in different volumes. | |
| 136 ret = CopyAndDeleteDirectory(from_path, to_path); | |
| 137 } | |
| 138 | |
| 139 if (!ret) { | |
| 140 // Leave a clue about what went wrong so that it can be (at least) picked | |
| 141 // up by a PLOG entry. | |
| 142 ::SetLastError(last_error); | |
| 143 } | |
| 144 | |
| 145 return ret; | |
| 146 } | |
| 147 | |
| 148 bool ReplaceFile(const FilePath& from_path, | 151 bool ReplaceFile(const FilePath& from_path, |
| 149 const FilePath& to_path, | 152 const FilePath& to_path, |
| 150 PlatformFileError* error) { | 153 PlatformFileError* error) { |
| 151 ThreadRestrictions::AssertIOAllowed(); | 154 ThreadRestrictions::AssertIOAllowed(); |
| 152 // Try a simple move first. It will only succeed when |to_path| doesn't | 155 // Try a simple move first. It will only succeed when |to_path| doesn't |
| 153 // already exist. | 156 // already exist. |
| 154 if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) | 157 if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) |
| 155 return true; | 158 return true; |
| 156 // Try the full-blown replace if the move fails, as ReplaceFile will only | 159 // Try the full-blown replace if the move fails, as ReplaceFile will only |
| 157 // succeed when |to_path| does exist. When writing to a network share, we may | 160 // succeed when |to_path| does exist. When writing to a network share, we may |
| 158 // not be able to change the ACLs. Ignore ACL errors then | 161 // not be able to change the ACLs. Ignore ACL errors then |
| 159 // (REPLACEFILE_IGNORE_MERGE_ERRORS). | 162 // (REPLACEFILE_IGNORE_MERGE_ERRORS). |
| 160 if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, | 163 if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, |
| 161 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { | 164 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { |
| 162 return true; | 165 return true; |
| 163 } | 166 } |
| 164 if (error) | 167 if (error) |
| 165 *error = LastErrorToPlatformFileError(GetLastError()); | 168 *error = LastErrorToPlatformFileError(GetLastError()); |
| 166 return false; | 169 return false; |
| 167 } | 170 } |
| 168 | 171 |
| 169 } // namespace base | |
| 170 | |
| 171 // ----------------------------------------------------------------------------- | |
| 172 | |
| 173 namespace file_util { | |
| 174 | |
| 175 using base::FilePath; | |
| 176 using base::kFileShareAll; | |
| 177 | |
| 178 bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { | |
| 179 base::ThreadRestrictions::AssertIOAllowed(); | |
| 180 | |
| 181 // NOTE: I suspect we could support longer paths, but that would involve | |
| 182 // analyzing all our usage of files. | |
| 183 if (from_path.value().length() >= MAX_PATH || | |
| 184 to_path.value().length() >= MAX_PATH) { | |
| 185 return false; | |
| 186 } | |
| 187 return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), | |
| 188 false) != 0); | |
| 189 } | |
| 190 | |
| 191 bool ShellCopy(const FilePath& from_path, const FilePath& to_path, | |
| 192 bool recursive) { | |
| 193 // WinXP SHFileOperation doesn't like trailing separators. | |
| 194 FilePath stripped_from = from_path.StripTrailingSeparators(); | |
| 195 FilePath stripped_to = to_path.StripTrailingSeparators(); | |
| 196 | |
| 197 base::ThreadRestrictions::AssertIOAllowed(); | |
| 198 | |
| 199 // NOTE: I suspect we could support longer paths, but that would involve | |
| 200 // analyzing all our usage of files. | |
| 201 if (stripped_from.value().length() >= MAX_PATH || | |
| 202 stripped_to.value().length() >= MAX_PATH) { | |
| 203 return false; | |
| 204 } | |
| 205 | |
| 206 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, | |
| 207 // so we have to use wcscpy because wcscpy_s writes non-NULLs | |
| 208 // into the rest of the buffer. | |
| 209 wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; | |
| 210 wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; | |
| 211 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation | |
| 212 wcscpy(double_terminated_path_from, stripped_from.value().c_str()); | |
| 213 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation | |
| 214 wcscpy(double_terminated_path_to, stripped_to.value().c_str()); | |
| 215 | |
| 216 SHFILEOPSTRUCT file_operation = {0}; | |
| 217 file_operation.wFunc = FO_COPY; | |
| 218 file_operation.pFrom = double_terminated_path_from; | |
| 219 file_operation.pTo = double_terminated_path_to; | |
| 220 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | | |
| 221 FOF_NOCONFIRMMKDIR; | |
| 222 if (!recursive) | |
| 223 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; | |
| 224 | |
| 225 return (SHFileOperation(&file_operation) == 0); | |
| 226 } | |
| 227 | |
| 228 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, | 172 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, |
| 229 bool recursive) { | 173 bool recursive) { |
| 230 base::ThreadRestrictions::AssertIOAllowed(); | 174 ThreadRestrictions::AssertIOAllowed(); |
| 231 | 175 |
| 232 if (recursive) | 176 if (recursive) |
| 233 return ShellCopy(from_path, to_path, true); | 177 return ShellCopy(from_path, to_path, true); |
| 234 | 178 |
| 235 // The following code assumes that from path is a directory. | 179 // The following code assumes that from path is a directory. |
| 236 DCHECK(DirectoryExists(from_path)); | 180 DCHECK(DirectoryExists(from_path)); |
| 237 | 181 |
| 238 // Instead of creating a new directory, we copy the old one to include the | 182 // Instead of creating a new directory, we copy the old one to include the |
| 239 // security information of the folder as part of the copy. | 183 // security information of the folder as part of the copy. |
| 240 if (!PathExists(to_path)) { | 184 if (!PathExists(to_path)) { |
| 241 // Except that Vista fails to do that, and instead do a recursive copy if | 185 // Except that Vista fails to do that, and instead do a recursive copy if |
| 242 // the target directory doesn't exist. | 186 // the target directory doesn't exist. |
| 243 if (base::win::GetVersion() >= base::win::VERSION_VISTA) | 187 if (base::win::GetVersion() >= base::win::VERSION_VISTA) |
| 244 CreateDirectory(to_path); | 188 CreateDirectory(to_path); |
| 245 else | 189 else |
| 246 ShellCopy(from_path, to_path, false); | 190 ShellCopy(from_path, to_path, false); |
| 247 } | 191 } |
| 248 | 192 |
| 249 FilePath directory = from_path.Append(L"*.*"); | 193 FilePath directory = from_path.Append(L"*.*"); |
| 250 return ShellCopy(directory, to_path, false); | 194 return ShellCopy(directory, to_path, false); |
| 251 } | 195 } |
| 252 | 196 |
| 253 bool CopyAndDeleteDirectory(const FilePath& from_path, | 197 } // namespace base |
| 254 const FilePath& to_path) { | |
| 255 base::ThreadRestrictions::AssertIOAllowed(); | |
| 256 if (CopyDirectory(from_path, to_path, true)) { | |
| 257 if (Delete(from_path, true)) { | |
| 258 return true; | |
| 259 } | |
| 260 // Like Move, this function is not transactional, so we just | |
| 261 // leave the copied bits behind if deleting from_path fails. | |
| 262 // If to_path exists previously then we have already overwritten | |
| 263 // it by now, we don't get better off by deleting the new bits. | |
| 264 } | |
| 265 return false; | |
| 266 } | |
| 267 | 198 |
| 199 // ----------------------------------------------------------------------------- |
| 200 |
| 201 namespace file_util { |
| 202 |
| 203 using base::FilePath; |
| 204 using base::kFileShareAll; |
| 268 | 205 |
| 269 bool PathExists(const FilePath& path) { | 206 bool PathExists(const FilePath& path) { |
| 270 base::ThreadRestrictions::AssertIOAllowed(); | 207 base::ThreadRestrictions::AssertIOAllowed(); |
| 271 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); | 208 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); |
| 272 } | 209 } |
| 273 | 210 |
| 274 bool PathIsWritable(const FilePath& path) { | 211 bool PathIsWritable(const FilePath& path) { |
| 275 base::ThreadRestrictions::AssertIOAllowed(); | 212 base::ThreadRestrictions::AssertIOAllowed(); |
| 276 HANDLE dir = | 213 HANDLE dir = |
| 277 CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, | 214 CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, |
| (...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 745 | 682 |
| 746 // Length of |path| with path separator appended. | 683 // Length of |path| with path separator appended. |
| 747 size_t prefix = path.StripTrailingSeparators().value().size() + 1; | 684 size_t prefix = path.StripTrailingSeparators().value().size() + 1; |
| 748 // The whole path string must be shorter than MAX_PATH. That is, it must be | 685 // The whole path string must be shorter than MAX_PATH. That is, it must be |
| 749 // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). | 686 // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). |
| 750 int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix)); | 687 int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix)); |
| 751 return std::min(whole_path_limit, static_cast<int>(max_length)); | 688 return std::min(whole_path_limit, static_cast<int>(max_length)); |
| 752 } | 689 } |
| 753 | 690 |
| 754 } // namespace file_util | 691 } // namespace file_util |
| 692 |
| 693 namespace base { |
| 694 namespace internal { |
| 695 |
| 696 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { |
| 697 ThreadRestrictions::AssertIOAllowed(); |
| 698 |
| 699 // NOTE: I suspect we could support longer paths, but that would involve |
| 700 // analyzing all our usage of files. |
| 701 if (from_path.value().length() >= MAX_PATH || |
| 702 to_path.value().length() >= MAX_PATH) { |
| 703 return false; |
| 704 } |
| 705 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), |
| 706 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) |
| 707 return true; |
| 708 |
| 709 // Keep the last error value from MoveFileEx around in case the below |
| 710 // fails. |
| 711 bool ret = false; |
| 712 DWORD last_error = ::GetLastError(); |
| 713 |
| 714 if (DirectoryExists(from_path)) { |
| 715 // MoveFileEx fails if moving directory across volumes. We will simulate |
| 716 // the move by using Copy and Delete. Ideally we could check whether |
| 717 // from_path and to_path are indeed in different volumes. |
| 718 ret = internal::CopyAndDeleteDirectory(from_path, to_path); |
| 719 } |
| 720 |
| 721 if (!ret) { |
| 722 // Leave a clue about what went wrong so that it can be (at least) picked |
| 723 // up by a PLOG entry. |
| 724 ::SetLastError(last_error); |
| 725 } |
| 726 |
| 727 return ret; |
| 728 } |
| 729 |
| 730 bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { |
| 731 ThreadRestrictions::AssertIOAllowed(); |
| 732 |
| 733 // NOTE: I suspect we could support longer paths, but that would involve |
| 734 // analyzing all our usage of files. |
| 735 if (from_path.value().length() >= MAX_PATH || |
| 736 to_path.value().length() >= MAX_PATH) { |
| 737 return false; |
| 738 } |
| 739 return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), |
| 740 false) != 0); |
| 741 } |
| 742 |
| 743 bool CopyAndDeleteDirectory(const FilePath& from_path, |
| 744 const FilePath& to_path) { |
| 745 ThreadRestrictions::AssertIOAllowed(); |
| 746 if (CopyDirectory(from_path, to_path, true)) { |
| 747 if (Delete(from_path, true)) { |
| 748 return true; |
| 749 } |
| 750 // Like Move, this function is not transactional, so we just |
| 751 // leave the copied bits behind if deleting from_path fails. |
| 752 // If to_path exists previously then we have already overwritten |
| 753 // it by now, we don't get better off by deleting the new bits. |
| 754 } |
| 755 return false; |
| 756 } |
| 757 |
| 758 } // namespace internal |
| 759 } // namespace base |
| OLD | NEW |