Index: runtime/bin/file_win.cc |
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc |
index 1c22ededcf24a42cf4e9f11b88c82581a71948d3..2e255293e7bd632d4427bf20d81fb1c7c5f99e81 100644 |
--- a/runtime/bin/file_win.cc |
+++ b/runtime/bin/file_win.cc |
@@ -12,6 +12,7 @@ |
#include <stdio.h> // NOLINT |
#include <string.h> // NOLINT |
#include <sys/stat.h> // NOLINT |
+#include <WinIoCtl.h> // NOLINT |
#include "bin/builtin.h" |
#include "bin/log.h" |
@@ -154,6 +155,107 @@ bool File::Create(const char* name) { |
} |
+// This structure is needed for creating and reading Junctions. |
+typedef struct _REPARSE_DATA_BUFFER { |
+ ULONG ReparseTag; |
+ USHORT ReparseDataLength; |
+ USHORT Reserved; |
+ |
+ union { |
+ struct { |
+ USHORT SubstituteNameOffset; |
+ USHORT SubstituteNameLength; |
+ USHORT PrintNameOffset; |
+ USHORT PrintNameLength; |
+ WCHAR PathBuffer[1]; |
+ } SymbolicLinkReparseBuffer; |
+ |
+ struct { |
+ USHORT SubstituteNameOffset; |
+ USHORT SubstituteNameLength; |
+ USHORT PrintNameOffset; |
+ USHORT PrintNameLength; |
+ WCHAR PathBuffer[1]; |
+ } MountPointReparseBuffer; |
+ |
+ struct { |
+ UCHAR DataBuffer[1]; |
+ } GenericReparseBuffer; |
+ }; |
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; |
+ |
+ |
+static const int kReparseDataHeaderSize = sizeof ULONG + 2 * sizeof USHORT; |
+static const int kMountPointHeaderSize = 4 * sizeof USHORT; |
+ |
+ |
+bool File::CreateLink(const char* utf8_name, const char* utf8_target) { |
+ const wchar_t* name = StringUtils::Utf8ToWide(utf8_name); |
+ int create_status = CreateDirectoryW(name, NULL); |
+ // If the directory already existed, treat it as a success. |
+ if (create_status == 0 && |
+ (GetLastError() != ERROR_ALREADY_EXISTS || |
+ (GetFileAttributesW(name) & FILE_ATTRIBUTE_DIRECTORY) != 0)) { |
+ free(const_cast<wchar_t*>(name)); |
+ return false; |
+ } |
+ |
+ HANDLE dir_handle = CreateFileW( |
+ name, |
+ GENERIC_READ | GENERIC_WRITE, |
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
+ NULL, |
+ OPEN_EXISTING, |
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, |
+ NULL); |
+ free(const_cast<wchar_t*>(name)); |
+ if (dir_handle == INVALID_HANDLE_VALUE) { |
+ return false; |
+ } |
+ |
+ const wchar_t* target = StringUtils::Utf8ToWide(utf8_target); |
+ int target_len = wcslen(target); |
+ if (target_len > MAX_PATH - 1) { |
+ free(const_cast<wchar_t*>(target)); |
+ CloseHandle(dir_handle); |
+ return false; |
+ } |
+ |
+ int reparse_data_buffer_size = |
+ sizeof REPARSE_DATA_BUFFER + 2 * MAX_PATH * sizeof WCHAR; |
+ REPARSE_DATA_BUFFER* reparse_data_buffer = |
+ static_cast<REPARSE_DATA_BUFFER*>(calloc(reparse_data_buffer_size, 1)); |
+ reparse_data_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; |
+ wcscpy(reparse_data_buffer->MountPointReparseBuffer.PathBuffer, target); |
+ wcscpy( |
+ reparse_data_buffer->MountPointReparseBuffer.PathBuffer + target_len + 1, |
+ target); |
+ reparse_data_buffer->MountPointReparseBuffer.SubstituteNameOffset = 0; |
+ reparse_data_buffer->MountPointReparseBuffer.SubstituteNameLength = |
+ target_len * sizeof WCHAR; |
+ reparse_data_buffer->MountPointReparseBuffer.PrintNameOffset = |
+ (target_len + 1) * sizeof WCHAR; |
+ reparse_data_buffer->MountPointReparseBuffer.PrintNameLength = |
+ target_len * sizeof WCHAR; |
+ reparse_data_buffer->ReparseDataLength = |
+ (target_len + 1) * 2 * sizeof WCHAR + kMountPointHeaderSize; |
+ DWORD dummy_received_bytes; |
+ int result = DeviceIoControl( |
+ dir_handle, |
+ FSCTL_SET_REPARSE_POINT, |
+ reparse_data_buffer, |
+ reparse_data_buffer->ReparseDataLength + kReparseDataHeaderSize, |
+ NULL, |
+ 0, |
+ &dummy_received_bytes, |
+ NULL); |
+ if (CloseHandle(dir_handle) == 0) return false; |
+ free(const_cast<wchar_t*>(target)); |
+ free(reparse_data_buffer); |
+ return (result != 0); |
+} |
+ |
+ |
bool File::Delete(const char* name) { |
const wchar_t* system_name = StringUtils::Utf8ToWide(name); |
int status = _wremove(system_name); |
@@ -279,6 +381,7 @@ File::Type File::GetType(const char* pathname, bool follow_links) { |
// TODO(whesse): Distinguish other errors from does not exist. |
return File::kDoesNotExist; |
} |
+ FindClose(find_handle); |
DWORD attributes = file_data.dwFileAttributes; |
if (!follow_links && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { |
DWORD reparse_tag = file_data.dwReserved0; |