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

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: windows 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
« no previous file with comments | « base/file_util_unittest.cc ('k') | base/prefs/json_pref_store_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
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
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
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
OLDNEW
« no previous file with comments | « base/file_util_unittest.cc ('k') | base/prefs/json_pref_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698