You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by jo...@apache.org on 2016/01/16 22:10:04 UTC
[3/3] mesos git commit: Windows: Implemented stout/os/stat.hpp`.
Windows: Implemented stout/os/stat.hpp`.
Review: https://reviews.apache.org/r/39803/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e6db3880
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e6db3880
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e6db3880
Branch: refs/heads/master
Commit: e6db3880b6ddf6b2cae6786eb206fcc0cf8ac7c6
Parents: 1b56d99
Author: Alex Clemmer <cl...@gmail.com>
Authored: Sat Jan 16 15:13:32 2016 -0500
Committer: Joris Van Remoortere <jo...@gmail.com>
Committed: Sat Jan 16 16:09:51 2016 -0500
----------------------------------------------------------------------
.../3rdparty/stout/include/Makefile.am | 2 +
.../stout/internal/windows/reparsepoint.hpp | 250 +++++++++++++++++++
.../include/stout/internal/windows/symlink.hpp | 87 +++++++
.../stout/include/stout/os/posix/stat.hpp | 12 +-
.../stout/include/stout/os/windows/stat.hpp | 103 +++++++-
.../3rdparty/stout/include/stout/windows.hpp | 77 +++++-
6 files changed, 507 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
index e6d5b42..06aa662 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
+++ b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am
@@ -39,6 +39,8 @@ nobase_include_HEADERS = \
stout/internal/windows/dirent.hpp \
stout/internal/windows/grp.hpp \
stout/internal/windows/pwd.hpp \
+ stout/internal/windows/symlink.hpp \
+ stout/internal/windows/reparsepoint.hpp \
stout/interval.hpp \
stout/ip.hpp \
stout/json.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/reparsepoint.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/reparsepoint.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/reparsepoint.hpp
new file mode 100644
index 0000000..36b0068
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/reparsepoint.hpp
@@ -0,0 +1,250 @@
+// 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.
+
+#ifndef __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
+#define __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
+
+#include <string>
+
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+
+namespace internal {
+namespace windows {
+
+// We pass this struct to `DeviceIoControl` to get information about a reparse
+// point (including things like whether it's a symlink). It is normally part of
+// the Device Driver Kit (DDK), specifically `nitfs.h`, but rather than taking
+// a dependency on the DDK, we choose to simply copy the struct here. This is a
+// well-worn path used by Boost FS[1], among others. See documentation
+// here[2].
+//
+// [1] http://www.boost.org/doc/libs/1_46_1/libs/filesystem/v3/src/operations.cpp
+// [2] https://msdn.microsoft.com/en-us/library/cc232007.aspx
+typedef struct _REPARSE_DATA_BUFFER
+{
+ // Describes, among other things, which type of reparse point this is (e.g.,
+ // a symlink).
+ ULONG ReparseTag;
+ // Size in bytes of common portion of the `REPARSE_DATA_BUFFER`.
+ USHORT ReparseDataLength;
+ // Unused. Ignore.
+ USHORT Reserved;
+ union
+ {
+ // Holds symlink data.
+ struct
+ {
+ // Byte offset in `PathBuffer` where the substitute name begins.
+ // Calculated as an offset from 0.
+ USHORT SubstituteNameOffset;
+ // Length in bytes of the substitute name.
+ USHORT SubstituteNameLength;
+ // Byte offset in `PathBuffer` where the print name begins. Calculated as
+ // an offset from 0.
+ USHORT PrintNameOffset;
+ // Length in bytes of the print name.
+ USHORT PrintNameLength;
+ // Indicates whether symlink is absolute or relative. If flags containing
+ // `SYMLINK_FLAG_RELATIVE`, then the substitute name is a relative
+ // symlink.
+ ULONG Flags;
+ // The first byte of the path string -- according to the documentation[1],
+ // this is followed in memory by the rest of the path string. The "path
+ // string" itself is a unicode char array containing both substitute name
+ // and print name. They can occur in any order. Use the offset and length
+ // of each in this struct to calculate where each starts and ends.
+ //
+ // [1] https://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+
+ // Unused: holds mount point data.
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct
+ {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER;
+
+#define REPARSE_MOUNTPOINT_HEADER_SIZE 8
+
+
+// Convenience struct for holding symlink data, meant purely for internal use.
+// We pass this around instead of the extremely inelegant `REPARSE_DATA_BUFFER`
+// struct, simply because this struct is easier to deal with and reason about.
+struct SymbolicLink
+{
+ std::wstring substitute_name;
+ std::wstring print_name;
+ ULONG flags;
+};
+
+
+// Checks file/folder attributes for a path to see if the reparse point
+// attribute is set; this indicates whether the path points at a reparse point,
+// rather than a "normal" file or folder.
+inline Try<bool> reparse_point_attribute_set(const std::string& absolute_path)
+{
+ const DWORD attributes = GetFileAttributes(absolute_path.c_str());
+ if (attributes == INVALID_FILE_ATTRIBUTES) {
+ return WindowsError(
+ "Failed to get attributes for file '" + absolute_path + "'");
+ }
+
+ return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+}
+
+
+// Attempts to extract symlink data out of a `REPARSE_DATA_BUFFER` (which could
+// hold other things, e.g., mount point data).
+inline Try<SymbolicLink> build_symbolic_link(const REPARSE_DATA_BUFFER& data)
+{
+ const bool is_symLink = (data.ReparseTag & IO_REPARSE_TAG_SYMLINK) != 0;
+ if (!is_symLink) {
+ return Error("Data buffer is not a symlink");
+ }
+
+ // NOTE: This buffer is not null terminated.
+ const WCHAR* substitute_name =
+ data.SymbolicLinkReparseBuffer.PathBuffer +
+ data.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
+ const size_t substitute_name_length =
+ data.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
+
+ // NOTE: This buffer is not null terminated.
+ const WCHAR* print_name =
+ data.SymbolicLinkReparseBuffer.PathBuffer +
+ data.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR);
+ const size_t print_name_length =
+ data.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR);
+
+ return SymbolicLink{
+ std::wstring(substitute_name, substitute_name_length),
+ std::wstring(print_name, print_name_length),
+ data.SymbolicLinkReparseBuffer.Flags};
+}
+
+
+// Attempts to get a file or folder handle for an absolute path, and does not
+// follow symlinks. That is, if the path points at a symlink, the handle will
+// refer to the symlink rather than the file or folder the symlink points at.
+inline Try<SharedHandle> get_handle_no_follow(const std::string& absolute_path)
+{
+ struct _stat s;
+ bool resolved_path_is_directory = false;
+ if (::_stat(absolute_path.c_str(), &s) >= 0) {
+ resolved_path_is_directory = S_ISDIR(s.st_mode);
+ }
+
+ // NOTE: The `CreateFile` documentation[1] tells us which flags we need to
+ // invoke to open a handle that will point at the symlink instead of the
+ // folder or file it points at. The short answer is that you need to be sure
+ // to pass in `OPEN_EXISTING` and `FILE_FLAG_OPEN_REPARSE_POINT` to get a
+ // handle for the symlink and not the file the symlink points to.
+ //
+ // Note also that `CreateFile` will appropriately generate a handle for
+ // either a folder or a file (they are different!), as long as you
+ // appropriately set a magic flag, `FILE_FLAG_BACKUP_SEMANTICS`. It is not
+ // clear why, or what this flag means, the documentation[1] only says it's
+ // necessary.
+ //
+ // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
+ const DWORD access_flags = resolved_path_is_directory
+ ? (FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
+ : FILE_FLAG_OPEN_REPARSE_POINT;
+
+ const HANDLE handle = CreateFile(
+ absolute_path.c_str(),
+ 0, // Ignored.
+ 0, // Ignored.
+ NULL, // Ignored.
+ OPEN_EXISTING, // Open existing symlink.
+ access_flags, // Open symlink, not the file it points to.
+ NULL); // Ignored.
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ return WindowsError(
+ "Could not open handle for symlink at path '" + absolute_path + "'");
+ }
+
+ return SharedHandle(handle, CloseHandle);
+}
+
+
+// Attempts to get the symlink data for a file or folder handle.
+inline Try<SymbolicLink> get_symbolic_link_data(const HANDLE handle)
+{
+ // To get the symlink data, we call `DeviceIoControl`. This function is part
+ // of the Device Driver Kit (DDK), and buried away in a corner of the
+ // DDK documentation[1] is an incomplete explanation of how to twist API to
+ // get it to emit information about reparse points (and, thus, symlinks,
+ // since symlinks are implemented with reparse points). This technique is a
+ // hack, but it is used pretty much everywhere, including the Boost FS code,
+ // though it's worth noting that they seem to use it incorrectly[2].
+ //
+ // Summarized, the documentation tells us that we need to pass in the magic
+ // flag `FSCTL_GET_REPARSE_POINT` to get the function to populate a
+ // `REPARSE_DATA_BUFFER` struct with data about a reparse point. What it
+ // doesn't tell you is that this struct is in a header in the DDK, so in
+ // order to get information about the reparse point, you must either take a
+ // dependency on the DDK, or copy the struct from the header into your code.
+ // We take a cue from Boost FS, and copy the struct into this header (see
+ // above).
+ //
+ // Finally, for context, it may be worth looking at the (sparse)
+ // documentation for `DeviceIoControl` itself.
+ //
+ // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
+ // [2] https://svn.boost.org/trac/boost/ticket/4663
+ // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
+
+ const size_t reparse_point_data_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
+ BYTE buffer[reparse_point_data_size];
+ REPARSE_DATA_BUFFER* reparse_point_data =
+ reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);
+
+ DWORD ignored = 0;
+
+ // The semantics of this function are: get the reparse data associated with
+ // the `handle` of some open directory or file, and that data in
+ // `reparse_point_data`.
+ const BOOL reparse_data_obtained = DeviceIoControl(
+ handle, // handle to file or directory
+ FSCTL_GET_REPARSE_POINT, // Gets reparse point data for file/folder handle.
+ NULL, // Ignored.
+ 0, // Ignored.
+ reparse_point_data,
+ reparse_point_data_size,
+ &ignored, // Ignored.
+ NULL); // Ignored.
+
+ if (!reparse_data_obtained) {
+ return WindowsError("Failed to obtain reparse point data for handle");
+ }
+
+ return build_symbolic_link(*reparse_point_data);
+}
+
+} // namespace windows {
+} // namespace internal {
+
+#endif // __STOUT_INTERNAL_WINDOWS_REPARSEPOINT_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/symlink.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/symlink.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/symlink.hpp
new file mode 100644
index 0000000..5dc022b
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/internal/windows/symlink.hpp
@@ -0,0 +1,87 @@
+// 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.
+
+#ifndef __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
+#define __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
+
+#include <string>
+
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/internal/windows/reparsepoint.hpp>
+
+#include <stout/os/realpath.hpp>
+
+
+namespace internal {
+namespace windows {
+
+// Gets symlink data for a given path, if it exists.
+//
+// This turns out to be a very complicated task on Windows. The gist of it is
+// that we know that symlinks on Windows are implemented with the Reparse Point
+// API, and so the process is a matter of:
+//
+// 1. Checking whether the attributes for the file/folder specified by the
+// path have the reparse point bit set; all symlinks are implemented with
+// reparse points, so this bit should be on all symlinks.
+// 2. Opening a file/folder handle for that path, instructing it specifically
+// to open a handle for the symlink (if the path points at a symlink) and
+// *not* the file the symlink points at (as is the default). Note that
+// file and folder handles are different, so we have a function that
+// chooses appropriately.
+// 3. Using `DeviceIoControl` to obtain information about the handle for this
+// reparse point, which we can then query to figure out if it's a reparse
+// point that is owned by the symlink filesystem filter driver.
+// 4. If it is, then we report that this path does point at a symlink.
+//
+// NOTE: it may be helpful to consult the documentation for each of these
+// functions, as they give you sources that justify the arguments to the
+// obscure APIs we call to get this all working.
+inline Try<SymbolicLink> query_symbolic_link_data(const std::string& path)
+{
+ // Convert to absolute path because Windows APIs expect it.
+ const Result<std::string> absolute_path = os::realpath(path);
+ if (!absolute_path.isSome()) {
+ return Error(absolute_path.error());
+ }
+
+ // Windows has no built-in way to tell whether a path points at a symbolic
+ // link; but, we know that symbolic links are implemented with reparse
+ // points, so we begin by checking that.
+ Try<bool> is_reparse_point =
+ reparse_point_attribute_set(absolute_path.get());
+
+ if (is_reparse_point.isError()) {
+ return Error(is_reparse_point.error());
+ } else if (!is_reparse_point.get()) {
+ return Error(
+ "Reparse point attribute is not set for path '" + absolute_path.get() +
+ "', and therefore it is not a symbolic link");
+ }
+
+ const Try<SharedHandle> symlink_handle =
+ get_handle_no_follow(absolute_path.get());
+
+ if (symlink_handle.isError()) {
+ return Error(symlink_handle.error());
+ }
+
+ // Finally, retrieve symlink data for the handle, if any.
+ return get_symbolic_link_data(symlink_handle.get().get_handle());
+}
+
+} // namespace windows {
+} // namespace internal {
+
+#endif // __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/stat.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/stat.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/stat.hpp
index ffe0748..32fa426 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/stat.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/posix/stat.hpp
@@ -34,6 +34,7 @@ inline bool isdir(const std::string& path)
if (::stat(path.c_str(), &s) < 0) {
return false;
}
+
return S_ISDIR(s.st_mode);
}
@@ -45,6 +46,7 @@ inline bool isfile(const std::string& path)
if (::stat(path.c_str(), &s) < 0) {
return false;
}
+
return S_ISREG(s.st_mode);
}
@@ -56,6 +58,7 @@ inline bool islink(const std::string& path)
if (::lstat(path.c_str(), &s) < 0) {
return false;
}
+
return S_ISLNK(s.st_mode);
}
@@ -83,21 +86,22 @@ inline Try<Bytes> size(
case DO_NOT_FOLLOW_SYMLINK: {
if (::lstat(path.c_str(), &s) < 0) {
return ErrnoError("Error invoking lstat for '" + path + "'");
+ } else {
+ return Bytes(s.st_size);
}
break;
}
case FOLLOW_SYMLINK: {
if (::stat(path.c_str(), &s) < 0) {
return ErrnoError("Error invoking stat for '" + path + "'");
+ } else {
+ return Bytes(s.st_size);
}
break;
}
- default: {
- UNREACHABLE();
- }
}
- return Bytes(s.st_size);
+ UNREACHABLE();
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/stat.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/stat.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/stat.hpp
index 5b38b9a..a2a5d0b 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/stat.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/windows/stat.hpp
@@ -15,29 +15,52 @@
#include <string>
+#include <stout/bytes.hpp>
#include <stout/try.hpp>
+#include <stout/unreachable.hpp>
+#include <stout/windows.hpp>
+#include <stout/internal/windows/reparsepoint.hpp>
+#include <stout/internal/windows/symlink.hpp>
+
+#ifdef _USE_32BIT_TIME_T
+#error "Implementation of `os::stat::mtime` assumes 64-bit `time_t`."
+#endif // _USE_32BIT_TIME_T
-namespace os {
+namespace os {
namespace stat {
inline bool isdir(const std::string& path)
{
- UNIMPLEMENTED;
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return false;
+ }
+
+ return S_ISDIR(s.st_mode);
}
inline bool isfile(const std::string& path)
{
- UNIMPLEMENTED;
-}
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return false;
+ }
+ return S_ISREG(s.st_mode);
+}
inline bool islink(const std::string& path)
{
- UNIMPLEMENTED;
+ Try<internal::windows::SymbolicLink> symlink =
+ internal::windows::query_symbolic_link_data(path);
+
+ return symlink.isSome();
}
@@ -58,35 +81,91 @@ inline Try<Bytes> size(
const std::string& path,
const FollowSymlink follow = FOLLOW_SYMLINK)
{
- UNIMPLEMENTED;
+ switch (follow) {
+ case DO_NOT_FOLLOW_SYMLINK: {
+ Try<internal::windows::SymbolicLink> symlink =
+ internal::windows::query_symbolic_link_data(path);
+
+ if (symlink.isError()) {
+ return Error(symlink.error());
+ } else {
+ return Bytes(symlink.get().substitute_name.length());
+ }
+ break;
+ }
+ case FOLLOW_SYMLINK: {
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return ErrnoError("Error invoking stat for '" + path + "'");
+ } else {
+ return Bytes(s.st_size);
+ }
+ break;
+ }
+ }
+
+ UNREACHABLE();
}
inline Try<long> mtime(const std::string& path)
{
- UNIMPLEMENTED;
+ Try<internal::windows::SymbolicLink> symlink =
+ internal::windows::query_symbolic_link_data(path);
+
+ if (symlink.isSome()) {
+ return Error(
+ "Requested mtime for '" + path +
+ "', but symbolic links don't have an mtime on Windows");
+ }
+
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return ErrnoError("Error invoking stat for '" + path + "'");
+ }
+
+ return s.st_mtime;
}
inline Try<mode_t> mode(const std::string& path)
{
- UNIMPLEMENTED;
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return ErrnoError("Error invoking stat for '" + path + "'");
+ }
+
+ return s.st_mode;
}
-inline Try<dev_t> rdev(const std::string& path)
+inline Try<dev_t> dev(const std::string& path)
{
- UNIMPLEMENTED;
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return ErrnoError("Error invoking stat for '" + path + "'");
+ }
+
+ return s.st_dev;
}
inline Try<ino_t> inode(const std::string& path)
{
- UNIMPLEMENTED;
+ struct _stat s;
+
+ if (::_stat(path.c_str(), &s) < 0) {
+ return ErrnoError("Error invoking stat for '" + path + "'");
+ }
+
+ return s.st_ino;
}
} // namespace stat {
-
} // namespace os {
#endif // __STOUT_OS_WINDOWS_STAT_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/e6db3880/3rdparty/libprocess/3rdparty/stout/include/stout/windows.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/windows.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/windows.hpp
index 27edf1b..061bb4a 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/windows.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/windows.hpp
@@ -27,6 +27,37 @@
#include <Winsock2.h>
#include <Windows.h>
+#include <memory>
+
+
+#ifdef _UNICODE
+// Much of the core Windows API is available both in `string` and `wstring`
+// varieties. To avoid polluting the namespace with two versions of every
+// function, a common pattern in the Windows headers is to offer a single macro
+// that expands to the `string` or `wstring` version, depending on whether the
+// `_UNICODE` preprocessor symbol is set. For example, `GetMessage` will expand
+// to either `GetMessageA` (the `string` version) or `GetMessageW` (the
+// `wstring` version) depending on whether this symbol is defined.
+//
+// The downside of this is that it makes POSIX interop really hard. Hence, we
+// refuse to compile if such a symbol is passed in during compilation.
+#error "Mesos doesn't currently support the `_UNICODE` Windows header constant"
+#endif // _UNICODE
+
+// An RAII `HANDLE`.
+class SharedHandle : public std::shared_ptr<void>
+{
+ static_assert(std::is_same<HANDLE, void*>::value,
+ "Expected `HANDLE` to be of type `void*`.");
+
+public:
+ template <typename Deleter>
+ SharedHandle(HANDLE handle, Deleter deleter)
+ : std::shared_ptr<void>(handle, deleter) {}
+
+ HANDLE get_handle() const { return this->get(); }
+};
+
// Definitions and constants used for Windows compat.
//
@@ -137,14 +168,44 @@ typedef SSIZE_T ssize_t;
// have to change any socket code.
constexpr int SHUT_RD = SD_RECEIVE;
-// Macros that test whether a `stat` struct represents a directory or a file.
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) // Directory.
-#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) // File.
-#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) // Character device.
-#define S_ISFIFO(mode) (((mode) & S_IFMT) == _S_IFIFO) // Pipe.
-#define S_ISBLK(mode) 0 // Block special device.
-#define S_ISSOCK(mode) 0 // Socket.
-#define S_ISLNK(mode) 0 // Symbolic link.
+// The following functions are usually macros on POSIX; we provide them here as
+// functions to avoid having global macros lying around. Note that these
+// operate on the `_stat` struct (a Windows version of the standard POSIX
+// `stat` struct), of which the `st_mode` field is known to be an `int`.
+inline bool S_ISDIR(const int mode)
+{
+ return (mode & S_IFMT) == S_IFDIR; // Directory.
+}
+
+inline bool S_ISREG(const int mode)
+{
+ return (mode & S_IFMT) == S_IFREG; // File.
+}
+
+inline bool S_ISCHR(const int mode)
+{
+ return (mode & S_IFMT) == S_IFCHR; // Character device.
+}
+
+inline bool S_ISFIFO(const int mode)
+{
+ return (mode & S_IFMT) == _S_IFIFO; // Pipe.
+}
+
+inline bool S_ISBLK(const int mode)
+{
+ return false; // Block special device.
+}
+
+inline bool S_ISSOCK(const int mode)
+{
+ return false; // Socket.
+}
+
+inline bool S_ISLNK(const int mode)
+{
+ return false; // Symbolic link.
+}
// Permissions API. (cf. MESOS-3176 to track ongoing permissions work.)
//