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.)
 //