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 |