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

Side by Side Diff: base/file_util_win.cc

Issue 141273010: Make CopyDirectory() not copy the read only bit on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Revert change to CopyFileUnsafe Created 6 years, 10 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') | no next file » | 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>
11 #include <time.h> 11 #include <time.h>
12 12
13 #include <algorithm> 13 #include <algorithm>
14 #include <limits> 14 #include <limits>
15 #include <string> 15 #include <string>
16 16
17 #include "base/files/file_enumerator.h"
17 #include "base/files/file_path.h" 18 #include "base/files/file_path.h"
18 #include "base/logging.h" 19 #include "base/logging.h"
19 #include "base/metrics/histogram.h" 20 #include "base/metrics/histogram.h"
20 #include "base/process/process_handle.h" 21 #include "base/process/process_handle.h"
21 #include "base/rand_util.h" 22 #include "base/rand_util.h"
22 #include "base/strings/string_number_conversions.h" 23 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h" 24 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h" 25 #include "base/strings/utf_string_conversions.h"
25 #include "base/threading/thread_restrictions.h" 26 #include "base/threading/thread_restrictions.h"
26 #include "base/time/time.h" 27 #include "base/time/time.h"
27 #include "base/win/scoped_handle.h" 28 #include "base/win/scoped_handle.h"
28 #include "base/win/windows_version.h" 29 #include "base/win/windows_version.h"
29 30
30 namespace base { 31 namespace base {
31 32
32 namespace { 33 namespace {
33 34
34 const DWORD kFileShareAll = 35 const DWORD kFileShareAll =
35 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 36 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
36 37
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
75 } // namespace 38 } // namespace
76 39
77 FilePath MakeAbsoluteFilePath(const FilePath& input) { 40 FilePath MakeAbsoluteFilePath(const FilePath& input) {
78 ThreadRestrictions::AssertIOAllowed(); 41 ThreadRestrictions::AssertIOAllowed();
79 wchar_t file_path[MAX_PATH]; 42 wchar_t file_path[MAX_PATH];
80 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) 43 if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
81 return FilePath(); 44 return FilePath();
82 return FilePath(file_path); 45 return FilePath(file_path);
83 } 46 }
84 47
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { 120 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
158 return true; 121 return true;
159 } 122 }
160 if (error) 123 if (error)
161 *error = File::OSErrorToFileError(GetLastError()); 124 *error = File::OSErrorToFileError(GetLastError());
162 return false; 125 return false;
163 } 126 }
164 127
165 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, 128 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
166 bool recursive) { 129 bool recursive) {
130 // NOTE(maruel): Previous version of this function used to call
131 // SHFileOperation(). This used to copy the file attributes and extended
132 // attributes, OLE structured storage, NTFS file system alternate data
133 // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we
134 // want the containing directory to propagate its SECURITY_DESCRIPTOR.
167 ThreadRestrictions::AssertIOAllowed(); 135 ThreadRestrictions::AssertIOAllowed();
168 136
169 if (recursive) 137 // NOTE: I suspect we could support longer paths, but that would involve
170 return ShellCopy(from_path, to_path, true); 138 // analyzing all our usage of files.
171 139 if (from_path.value().length() >= MAX_PATH ||
172 // The following code assumes that from path is a directory. 140 to_path.value().length() >= MAX_PATH) {
173 DCHECK(DirectoryExists(from_path)); 141 return false;
174
175 // Instead of creating a new directory, we copy the old one to include the
176 // security information of the folder as part of the copy.
177 if (!PathExists(to_path)) {
178 // Except that Vista fails to do that, and instead do a recursive copy if
179 // the target directory doesn't exist.
180 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
181 CreateDirectory(to_path);
182 else
183 ShellCopy(from_path, to_path, false);
184 } 142 }
185 143
186 FilePath directory = from_path.Append(L"*.*"); 144 // This function does not properly handle destinations within the source.
187 return ShellCopy(directory, to_path, false); 145 FilePath real_to_path = to_path;
146 if (PathExists(real_to_path)) {
147 real_to_path = MakeAbsoluteFilePath(real_to_path);
148 if (real_to_path.empty())
149 return false;
150 } else {
151 real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
152 if (real_to_path.empty())
153 return false;
154 }
155 FilePath real_from_path = MakeAbsoluteFilePath(from_path);
156 if (real_from_path.empty())
157 return false;
158 if (real_to_path.value().size() >= real_from_path.value().size() &&
159 real_to_path.value().compare(0, real_from_path.value().size(),
160 real_from_path.value()) == 0) {
161 return false;
162 }
163
164 int traverse_type = FileEnumerator::FILES;
165 if (recursive)
166 traverse_type |= FileEnumerator::DIRECTORIES;
167 FileEnumerator traversal(from_path, recursive, traverse_type);
168
169 if (!PathExists(from_path)) {
170 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
171 << from_path.value().c_str();
172 return false;
173 }
174 // TODO(maruel): This is not necessary anymore.
175 DCHECK(recursive || DirectoryExists(from_path));
176
177 FilePath current = from_path;
178 bool from_is_dir = DirectoryExists(from_path);
179 bool success = true;
180 FilePath from_path_base = from_path;
181 if (recursive && DirectoryExists(to_path)) {
182 // If the destination already exists and is a directory, then the
183 // top level of source needs to be copied.
184 from_path_base = from_path.DirName();
185 }
186
187 while (success && !current.empty()) {
188 // current is the source path, including from_path, so append
189 // the suffix after from_path to to_path to create the target_path.
190 FilePath target_path(to_path);
191 if (from_path_base != current) {
192 if (!from_path_base.AppendRelativePath(current, &target_path)) {
193 success = false;
194 break;
195 }
196 }
197
198 if (from_is_dir) {
199 if (!DirectoryExists(target_path) &&
200 !::CreateDirectory(target_path.value().c_str(), NULL)) {
201 DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
202 << target_path.value().c_str();
203 success = false;
204 }
205 } else if (!internal::CopyFileUnsafe(current, target_path)) {
206 DLOG(ERROR) << "CopyDirectory() couldn't create file: "
207 << target_path.value().c_str();
208 success = false;
209 }
210
211 current = traversal.Next();
212 if (!current.empty())
213 from_is_dir = traversal.GetInfo().IsDirectory();
214 }
215
216 return success;
188 } 217 }
189 218
190 bool PathExists(const FilePath& path) { 219 bool PathExists(const FilePath& path) {
191 ThreadRestrictions::AssertIOAllowed(); 220 ThreadRestrictions::AssertIOAllowed();
192 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); 221 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
193 } 222 }
194 223
195 bool PathIsWritable(const FilePath& path) { 224 bool PathIsWritable(const FilePath& path) {
196 ThreadRestrictions::AssertIOAllowed(); 225 ThreadRestrictions::AssertIOAllowed();
197 HANDLE dir = 226 HANDLE dir =
(...skipping 560 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 // Like Move, this function is not transactional, so we just 787 // Like Move, this function is not transactional, so we just
759 // leave the copied bits behind if deleting from_path fails. 788 // leave the copied bits behind if deleting from_path fails.
760 // If to_path exists previously then we have already overwritten 789 // If to_path exists previously then we have already overwritten
761 // it by now, we don't get better off by deleting the new bits. 790 // it by now, we don't get better off by deleting the new bits.
762 } 791 }
763 return false; 792 return false;
764 } 793 }
765 794
766 } // namespace internal 795 } // namespace internal
767 } // namespace base 796 } // namespace base
OLDNEW
« no previous file with comments | « base/file_util_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698