diff options
author | 2017-06-28 16:05:23 +0200 | |
---|---|---|
committer | 2017-06-29 09:25:27 +0200 | |
commit | f07023471d261abbefee65adb2ca769b1da0ba42 (patch) | |
tree | 5450696a88dacabfcfa65a008e9edb0a4b1450ff /src/main/native/windows/file.cc | |
parent | 1e0628d18c2e3defaddd935ffb41edbf3ef40339 (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.cc | 186 |
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 |