Index: base/file_util_win.cc |
diff --git a/base/file_util_win.cc b/base/file_util_win.cc |
index fcad39c9b2f3686e7c1ecc824bf8db8e242c9ded..dc15ea069807939115867ad4104335ba234aa8ac 100644 |
--- a/base/file_util_win.cc |
+++ b/base/file_util_win.cc |
@@ -6,6 +6,7 @@ |
#include <windows.h> |
#include <propvarutil.h> |
+#include <psapi.h> |
#include <shellapi.h> |
#include <shlobj.h> |
#include <time.h> |
@@ -21,6 +22,49 @@ |
namespace file_util { |
+namespace { |
+ |
+// Helper for NormalizeFilePath(), defined below. |
+bool DevicePathToDriveLetterPath(const FilePath& device_path, |
+ FilePath* drive_letter_path) { |
+ // Get the mapping of drive letters to device paths. |
+ const int kDriveMappingSize = 1024; |
+ wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; |
+ if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { |
+ LOG(ERROR) << "Failed to get drive mapping."; |
+ return false; |
+ } |
+ |
+ // The drive mapping is a sequence of null terminated strings. |
+ // The last string is empty. |
+ wchar_t* drive_map_ptr = drive_mapping; |
+ wchar_t device_name[MAX_PATH]; |
+ wchar_t drive[] = L" :"; |
+ |
+ // For each string in the drive mapping, get the junction that links |
+ // to it. If that junction is a prefix of |device_path|, then we |
+ // know that |drive| is the real path prefix. |
+ while(*drive_map_ptr) { |
+ drive[0] = drive_map_ptr[0]; // Copy the drive letter. |
+ |
+ if (QueryDosDevice(drive, device_name, MAX_PATH) && |
+ StartsWith(device_path.value(), device_name, true)) { |
+ *drive_letter_path = FilePath(drive + |
+ device_path.value().substr(wcslen(device_name))); |
+ return true; |
+ } |
+ // Move to the next drive letter string, which starts one |
+ // increment after the '\0' that terminates the current string. |
+ while(*drive_map_ptr++); |
+ } |
+ |
+ // No drive matched. The path does not start with a device junction. |
+ *drive_letter_path = device_path; |
+ return true; |
+} |
+ |
+} // namespace |
+ |
std::wstring GetDirectoryFromPath(const std::wstring& path) { |
wchar_t path_buffer[MAX_PATH]; |
wchar_t* file_ptr = NULL; |
@@ -137,10 +181,14 @@ bool Move(const FilePath& from_path, const FilePath& to_path) { |
bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { |
// Make sure that the target file exists. |
- HANDLE target_file = ::CreateFile(to_path.value().c_str(), 0, |
- FILE_SHARE_READ | FILE_SHARE_WRITE, |
- NULL, CREATE_NEW, |
- FILE_ATTRIBUTE_NORMAL, NULL); |
+ HANDLE target_file = ::CreateFile( |
+ to_path.value().c_str(), |
+ 0, |
+ FILE_SHARE_READ | FILE_SHARE_WRITE, |
+ NULL, |
+ CREATE_NEW, |
+ FILE_ATTRIBUTE_NORMAL, |
+ NULL); |
if (target_file != INVALID_HANDLE_VALUE) |
::CloseHandle(target_file); |
// When writing to a network share, we may not be able to change the ACLs. |
@@ -892,4 +940,73 @@ bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info, |
return result == 1 || result == 0; |
} |
+bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { |
+ ScopedHandle path_handle( |
+ ::CreateFile(path.value().c_str(), |
+ GENERIC_READ, |
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
+ NULL, |
+ OPEN_EXISTING, |
+ FILE_ATTRIBUTE_NORMAL, |
+ NULL)); |
+ if (path_handle == INVALID_HANDLE_VALUE) |
+ return false; |
+ |
+ // In Vista, GetFinalPathNameByHandle() would give us the real path |
+ // from a file handle. If we ever deprecate XP, consider changing the |
+ // code below to a call to GetFinalPathNameByHandle(). The method this |
+ // function uses is explained in the following msdn article: |
+ // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx |
+ DWORD file_size_high = 0; |
+ DWORD file_size_low = ::GetFileSize(path_handle.Get(), &file_size_high); |
+ if (file_size_low == 0 && file_size_high == 0) { |
+ // It is not possible to map an empty file. |
+ LOG(ERROR) << "NormalizeFilePath failed: Empty file."; |
+ return false; |
+ } |
+ |
+ // Create a file mapping object. Can't easily use MemoryMappedFile, because |
+ // we only map the first byte, and need direct access to the handle. |
+ ScopedHandle file_map_handle( |
+ ::CreateFileMapping(path_handle.Get(), |
+ NULL, |
+ PAGE_READONLY, |
+ 0, |
+ 1, // Just one byte. No need to look at the data. |
+ NULL)); |
+ |
+ if (file_map_handle == INVALID_HANDLE_VALUE) |
+ return false; |
+ |
+ // Use a view of the file to get the path to the file. |
+ void* file_view = MapViewOfFile( |
+ file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1); |
+ if (!file_view) |
+ return false; |
+ |
+ bool success = false; |
+ |
+ // The expansion of |path| into a full path may make it longer. |
+ // GetMappedFileName() will fail if the result is longer than MAX_PATH. |
+ // Pad a bit to be safe. If kMaxPathLength is ever changed to be less |
+ // than MAX_PATH, it would be nessisary to test that GetMappedFileName() |
+ // not return kMaxPathLength. This would mean that only part of the |
+ // path fit in |mapped_file_path|. |
+ const int kMaxPathLength = MAX_PATH + 10; |
+ wchar_t mapped_file_path[kMaxPathLength]; |
+ if (::GetMappedFileName(GetCurrentProcess(), |
+ file_view, |
+ mapped_file_path, |
+ kMaxPathLength)) { |
+ // GetMappedFileName() will return a path that starts with |
+ // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() |
+ // will find a drive letter which maps to the path's device, so |
+ // that we return a path starting with a drive letter. |
+ FilePath mapped_file(mapped_file_path); |
+ success = DevicePathToDriveLetterPath(mapped_file, real_path); |
+ } |
+ UnmapViewOfFile(file_view); |
+ return success; |
+} |
+ |
} // namespace file_util |