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