aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/native/windows/file.cc
diff options
context:
space:
mode:
authorGravatar Laszlo Csomor <laszlocsomor@google.com>2017-06-28 16:05:23 +0200
committerGravatar Marcel Hlopko <hlopko@google.com>2017-06-29 09:25:27 +0200
commitf07023471d261abbefee65adb2ca769b1da0ba42 (patch)
tree5450696a88dacabfcfa65a008e9edb0a4b1450ff /src/main/native/windows/file.cc
parent1e0628d18c2e3defaddd935ffb41edbf3ef40339 (diff)
Windows, JNI: move around sources
Move the Windows JNI C++ sources to a separate package and separate namespace. This no-op refactoring allows other build rules than Bazel's client library to depend on file I/O and/or JNI functionality. A follow-up commit will split the //src/main/native/windows:processes library into :jni-processes and :jni-file. Change-Id: I33c5f8ebd8961cc440db3b4a95ff78024d7c1d74 PiperOrigin-RevId: 160404298
Diffstat (limited to 'src/main/native/windows/file.cc')
-rw-r--r--src/main/native/windows/file.cc186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc
new file mode 100644
index 0000000000..b5dc5974c8
--- /dev/null
+++ b/src/main/native/windows/file.cc
@@ -0,0 +1,186 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <windows.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "src/main/native/windows/file.h"
+#include "src/main/native/windows/util.h"
+
+namespace bazel {
+namespace windows {
+
+using std::string;
+using std::unique_ptr;
+using std::wstring;
+
+int IsJunctionOrDirectorySymlink(const WCHAR* path) {
+ DWORD attrs = ::GetFileAttributesW(path);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ return IS_JUNCTION_ERROR;
+ } else {
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) &&
+ (attrs & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ return IS_JUNCTION_YES;
+ } else {
+ return IS_JUNCTION_NO;
+ }
+ }
+}
+
+bool GetLongPath(const WCHAR* path, unique_ptr<WCHAR[]>* result) {
+ DWORD size = ::GetLongPathNameW(path, NULL, 0);
+ if (size == 0) {
+ return false;
+ }
+ result->reset(new WCHAR[size]);
+ ::GetLongPathNameW(path, result->get(), size);
+ return true;
+}
+
+HANDLE OpenDirectory(const WCHAR* path, bool read_write) {
+ return ::CreateFileW(
+ /* lpFileName */ path,
+ /* dwDesiredAccess */
+ read_write ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
+ /* dwShareMode */ 0,
+ /* lpSecurityAttributes */ NULL,
+ /* dwCreationDisposition */ OPEN_EXISTING,
+ /* dwFlagsAndAttributes */ FILE_FLAG_OPEN_REPARSE_POINT |
+ FILE_FLAG_BACKUP_SEMANTICS,
+ /* hTemplateFile */ NULL);
+}
+
+#pragma pack(push, 4)
+typedef struct _JunctionDescription {
+ typedef struct _Header {
+ DWORD ReparseTag;
+ WORD ReparseDataLength;
+ WORD Reserved;
+ } Header;
+
+ typedef struct _WriteDesc {
+ WORD SubstituteNameOffset;
+ WORD SubstituteNameLength;
+ WORD PrintNameOffset;
+ WORD PrintNameLength;
+ } WriteDesc;
+
+ Header header;
+ WriteDesc write;
+ WCHAR PathBuffer[ANYSIZE_ARRAY];
+} JunctionDescription;
+#pragma pack(pop)
+
+string CreateJunction(const wstring& junction_name,
+ const wstring& junction_target) {
+ const wstring target = HasUncPrefix(junction_target.c_str())
+ ? junction_target.substr(4)
+ : junction_target;
+ // The entire JunctionDescription cannot be larger than
+ // MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes.
+ //
+ // The structure's layout is:
+ // [JunctionDescription::Header]
+ // [JunctionDescription::WriteDesc]
+ // ---- start of JunctionDescription::PathBuffer ----
+ // [4 WCHARs] : "\??\" prefix
+ // [target.size() WCHARs] : junction target name
+ // [1 WCHAR] : null-terminator
+ // [target.size() WCHARs] : junction target displayed name
+ // [1 WCHAR] : null-terminator
+ // The sum of these must not exceed MAXIMUM_REPARSE_DATA_BUFFER_SIZE.
+ // We can rearrange this to get the limit for target.size().
+ static const size_t kMaxJunctionTargetLen =
+ ((MAXIMUM_REPARSE_DATA_BUFFER_SIZE - sizeof(JunctionDescription::Header) -
+ sizeof(JunctionDescription::WriteDesc) -
+ /* one "\??\" prefix */ sizeof(WCHAR) * 4 -
+ /* two null terminators */ sizeof(WCHAR) * 2) /
+ /* two copies of the string are stored */ 2) /
+ sizeof(WCHAR);
+ if (target.size() > kMaxJunctionTargetLen) {
+ std::stringstream error;
+ error << "junction target is too long (" << target.size()
+ << " characters, limit: " << kMaxJunctionTargetLen << ")";
+ return error.str();
+ }
+ const wstring name = HasUncPrefix(junction_name.c_str())
+ ? junction_name
+ : (wstring(L"\\\\?\\") + junction_name);
+
+ // Junctions are directories, so create one
+ if (!::CreateDirectoryW(name.c_str(), NULL)) {
+ return string("CreateDirectoryW failed");
+ }
+
+ AutoHandle handle(OpenDirectory(name.c_str(), true));
+ if (!handle.IsValid()) {
+ return string("OpenDirectory failed");
+ }
+
+ char reparse_buffer_bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ JunctionDescription* reparse_buffer =
+ reinterpret_cast<JunctionDescription*>(reparse_buffer_bytes);
+ memset(reparse_buffer_bytes, 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+
+ // "\??\" is meaningful to the kernel, it's a synomym for the "\DosDevices\"
+ // object path. (NOT to be confused with "\\?\" which is meaningful for the
+ // Win32 API.) We need to use this prefix to tell the kernel where the reparse
+ // point is pointing to.
+ memcpy(reparse_buffer->PathBuffer, L"\\??\\", 4 * sizeof(WCHAR));
+ memcpy(reparse_buffer->PathBuffer + 4, target.c_str(),
+ target.size() * sizeof(WCHAR));
+
+ // In addition to their target, junctions also have another string which is a
+ // user-visible name of where the junction points, as listed by "dir". This
+ // can be any string and won't affect the usability of the junction.
+ // MKLINK uses the target path without the "\??\" prefix as the display name,
+ // so let's do that here too. This is also in line with how UNIX behaves.
+ // Using a dummy or fake display name would be pure evil, it would make the
+ // output of `dir` look like:
+ // 2017-01-18 01:37 PM <JUNCTION> juncname [dummy string]
+ memcpy(reparse_buffer->PathBuffer + 4 + target.size() + 1, target.c_str(),
+ target.size() * sizeof(WCHAR));
+
+ reparse_buffer->write.SubstituteNameOffset = 0;
+ reparse_buffer->write.SubstituteNameLength =
+ (4 + target.size()) * sizeof(WCHAR);
+ reparse_buffer->write.PrintNameOffset =
+ reparse_buffer->write.SubstituteNameLength +
+ /* null-terminator */ sizeof(WCHAR);
+ reparse_buffer->write.PrintNameLength = target.size() * sizeof(WCHAR);
+
+ reparse_buffer->header.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ reparse_buffer->header.ReparseDataLength =
+ sizeof(JunctionDescription::WriteDesc) +
+ reparse_buffer->write.SubstituteNameLength +
+ reparse_buffer->write.PrintNameLength +
+ /* 2 null-terminators */ (2 * sizeof(WCHAR));
+ reparse_buffer->header.Reserved = 0;
+
+ DWORD bytes_returned;
+ if (!::DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, reparse_buffer,
+ reparse_buffer->header.ReparseDataLength +
+ sizeof(JunctionDescription::Header),
+ NULL, 0, &bytes_returned, NULL)) {
+ return string("DeviceIoControl(FSCTL_SET_REPARSE_POINT) failed");
+ }
+ return "";
+}
+
+} // namespace windows
+} // namespace bazel