OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 <propvarutil.h> | 8 #include <propvarutil.h> |
| 9 #include <psapi.h> |
9 #include <shellapi.h> | 10 #include <shellapi.h> |
10 #include <shlobj.h> | 11 #include <shlobj.h> |
11 #include <time.h> | 12 #include <time.h> |
12 #include <string> | 13 #include <string> |
13 | 14 |
14 #include "base/file_path.h" | 15 #include "base/file_path.h" |
15 #include "base/logging.h" | 16 #include "base/logging.h" |
16 #include "base/scoped_comptr_win.h" | 17 #include "base/scoped_comptr_win.h" |
17 #include "base/scoped_handle.h" | 18 #include "base/scoped_handle.h" |
18 #include "base/string_util.h" | 19 #include "base/string_util.h" |
19 #include "base/time.h" | 20 #include "base/time.h" |
20 #include "base/win_util.h" | 21 #include "base/win_util.h" |
21 | 22 |
22 namespace file_util { | 23 namespace file_util { |
23 | 24 |
| 25 namespace { |
| 26 |
| 27 // Helper for NormalizeFilePath(), defined below. |
| 28 bool DevicePathToDriveLetterPath(const FilePath& device_path, |
| 29 FilePath* drive_letter_path) { |
| 30 // Get the mapping of drive letters to device paths. |
| 31 const int kDriveMappingSize = 1024; |
| 32 wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; |
| 33 if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { |
| 34 LOG(ERROR) << "Failed to get drive mapping."; |
| 35 return false; |
| 36 } |
| 37 |
| 38 // The drive mapping is a sequence of null terminated strings. |
| 39 // The last string is empty. |
| 40 wchar_t* drive_map_ptr = drive_mapping; |
| 41 wchar_t device_name[MAX_PATH]; |
| 42 wchar_t drive[] = L" :"; |
| 43 |
| 44 // For each string in the drive mapping, get the junction that links |
| 45 // to it. If that junction is a prefix of |device_path|, then we |
| 46 // know that |drive| is the real path prefix. |
| 47 while(*drive_map_ptr) { |
| 48 drive[0] = drive_map_ptr[0]; // Copy the drive letter. |
| 49 |
| 50 if (QueryDosDevice(drive, device_name, MAX_PATH) && |
| 51 StartsWith(device_path.value(), device_name, true)) { |
| 52 *drive_letter_path = FilePath(drive + |
| 53 device_path.value().substr(wcslen(device_name))); |
| 54 return true; |
| 55 } |
| 56 // Move to the next drive letter string, which starts one |
| 57 // increment after the '\0' that terminates the current string. |
| 58 while(*drive_map_ptr++); |
| 59 } |
| 60 |
| 61 // No drive matched. The path does not start with a device junction. |
| 62 *drive_letter_path = device_path; |
| 63 return true; |
| 64 } |
| 65 |
| 66 } // namespace |
| 67 |
24 std::wstring GetDirectoryFromPath(const std::wstring& path) { | 68 std::wstring GetDirectoryFromPath(const std::wstring& path) { |
25 wchar_t path_buffer[MAX_PATH]; | 69 wchar_t path_buffer[MAX_PATH]; |
26 wchar_t* file_ptr = NULL; | 70 wchar_t* file_ptr = NULL; |
27 if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0) | 71 if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0) |
28 return L""; | 72 return L""; |
29 | 73 |
30 std::wstring::size_type length = | 74 std::wstring::size_type length = |
31 file_ptr ? file_ptr - path_buffer : path.length(); | 75 file_ptr ? file_ptr - path_buffer : path.length(); |
32 std::wstring directory(path, 0, length); | 76 std::wstring directory(path, 0, length); |
33 return FilePath(directory).StripTrailingSeparators().value(); | 77 return FilePath(directory).StripTrailingSeparators().value(); |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 // MoveFileEx fails if moving directory across volumes. We will simulate | 174 // MoveFileEx fails if moving directory across volumes. We will simulate |
131 // the move by using Copy and Delete. Ideally we could check whether | 175 // the move by using Copy and Delete. Ideally we could check whether |
132 // from_path and to_path are indeed in different volumes. | 176 // from_path and to_path are indeed in different volumes. |
133 return CopyAndDeleteDirectory(from_path, to_path); | 177 return CopyAndDeleteDirectory(from_path, to_path); |
134 } | 178 } |
135 return false; | 179 return false; |
136 } | 180 } |
137 | 181 |
138 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { | 182 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { |
139 // Make sure that the target file exists. | 183 // Make sure that the target file exists. |
140 HANDLE target_file = ::CreateFile(to_path.value().c_str(), 0, | 184 HANDLE target_file = ::CreateFile( |
141 FILE_SHARE_READ | FILE_SHARE_WRITE, | 185 to_path.value().c_str(), |
142 NULL, CREATE_NEW, | 186 0, |
143 FILE_ATTRIBUTE_NORMAL, NULL); | 187 FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 188 NULL, |
| 189 CREATE_NEW, |
| 190 FILE_ATTRIBUTE_NORMAL, |
| 191 NULL); |
144 if (target_file != INVALID_HANDLE_VALUE) | 192 if (target_file != INVALID_HANDLE_VALUE) |
145 ::CloseHandle(target_file); | 193 ::CloseHandle(target_file); |
146 // When writing to a network share, we may not be able to change the ACLs. | 194 // When writing to a network share, we may not be able to change the ACLs. |
147 // Ignore ACL errors then (REPLACEFILE_IGNORE_MERGE_ERRORS). | 195 // Ignore ACL errors then (REPLACEFILE_IGNORE_MERGE_ERRORS). |
148 return ::ReplaceFile(to_path.value().c_str(), | 196 return ::ReplaceFile(to_path.value().c_str(), |
149 from_path.value().c_str(), NULL, | 197 from_path.value().c_str(), NULL, |
150 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL) ? true : false; | 198 REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL) ? true : false; |
151 } | 199 } |
152 | 200 |
153 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { | 201 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { |
(...skipping 731 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
885 length_ = INVALID_FILE_SIZE; | 933 length_ = INVALID_FILE_SIZE; |
886 } | 934 } |
887 | 935 |
888 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info, | 936 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info, |
889 const base::Time& cutoff_time) { | 937 const base::Time& cutoff_time) { |
890 long result = CompareFileTime(&find_info.ftLastWriteTime, | 938 long result = CompareFileTime(&find_info.ftLastWriteTime, |
891 &cutoff_time.ToFileTime()); | 939 &cutoff_time.ToFileTime()); |
892 return result == 1 || result == 0; | 940 return result == 1 || result == 0; |
893 } | 941 } |
894 | 942 |
| 943 bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { |
| 944 ScopedHandle path_handle( |
| 945 ::CreateFile(path.value().c_str(), |
| 946 GENERIC_READ, |
| 947 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
| 948 NULL, |
| 949 OPEN_EXISTING, |
| 950 FILE_ATTRIBUTE_NORMAL, |
| 951 NULL)); |
| 952 if (path_handle == INVALID_HANDLE_VALUE) |
| 953 return false; |
| 954 |
| 955 // In Vista, GetFinalPathNameByHandle() would give us the real path |
| 956 // from a file handle. If we ever deprecate XP, consider changing the |
| 957 // code below to a call to GetFinalPathNameByHandle(). The method this |
| 958 // function uses is explained in the following msdn article: |
| 959 // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx |
| 960 DWORD file_size_high = 0; |
| 961 DWORD file_size_low = ::GetFileSize(path_handle.Get(), &file_size_high); |
| 962 if (file_size_low == 0 && file_size_high == 0) { |
| 963 // It is not possible to map an empty file. |
| 964 LOG(ERROR) << "NormalizeFilePath failed: Empty file."; |
| 965 return false; |
| 966 } |
| 967 |
| 968 // Create a file mapping object. Can't easily use MemoryMappedFile, because |
| 969 // we only map the first byte, and need direct access to the handle. |
| 970 ScopedHandle file_map_handle( |
| 971 ::CreateFileMapping(path_handle.Get(), |
| 972 NULL, |
| 973 PAGE_READONLY, |
| 974 0, |
| 975 1, // Just one byte. No need to look at the data. |
| 976 NULL)); |
| 977 |
| 978 if (file_map_handle == INVALID_HANDLE_VALUE) |
| 979 return false; |
| 980 |
| 981 // Use a view of the file to get the path to the file. |
| 982 void* file_view = MapViewOfFile( |
| 983 file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1); |
| 984 if (!file_view) |
| 985 return false; |
| 986 |
| 987 bool success = false; |
| 988 |
| 989 // The expansion of |path| into a full path may make it longer. |
| 990 // GetMappedFileName() will fail if the result is longer than MAX_PATH. |
| 991 // Pad a bit to be safe. If kMaxPathLength is ever changed to be less |
| 992 // than MAX_PATH, it would be nessisary to test that GetMappedFileName() |
| 993 // not return kMaxPathLength. This would mean that only part of the |
| 994 // path fit in |mapped_file_path|. |
| 995 const int kMaxPathLength = MAX_PATH + 10; |
| 996 wchar_t mapped_file_path[kMaxPathLength]; |
| 997 if (::GetMappedFileName(GetCurrentProcess(), |
| 998 file_view, |
| 999 mapped_file_path, |
| 1000 kMaxPathLength)) { |
| 1001 // GetMappedFileName() will return a path that starts with |
| 1002 // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() |
| 1003 // will find a drive letter which maps to the path's device, so |
| 1004 // that we return a path starting with a drive letter. |
| 1005 FilePath mapped_file(mapped_file_path); |
| 1006 success = DevicePathToDriveLetterPath(mapped_file, real_path); |
| 1007 } |
| 1008 UnmapViewOfFile(file_view); |
| 1009 return success; |
| 1010 } |
| 1011 |
895 } // namespace file_util | 1012 } // namespace file_util |
OLD | NEW |