Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Side by Side Diff: base/file_util_win.cc

Issue 18332014: Move Copy* into the base namespace. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698