Apache Mesos
symlink.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
14 #define __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
15 
16 #include <string>
17 
18 #include <stout/try.hpp>
19 #include <stout/windows.hpp>
20 
23 
24 
25 namespace internal {
26 namespace windows {
27 
28 // Get the full / absolute path. Does not check for existence, and does not
29 // resolve symlinks.
30 inline Result<std::string> fullpath(const std::string& path)
31 {
32  // First query for the buffer size required.
33  const DWORD length =
34  ::GetFullPathNameW(longpath(path).data(), 0, nullptr, nullptr);
35 
36  if (length == 0) {
37  return WindowsError("Failed to retrieve fullpath buffer size");
38  }
39 
40  std::vector<wchar_t> buffer;
41  buffer.reserve(static_cast<size_t>(length));
42 
43  const DWORD result =
44  ::GetFullPathNameW(longpath(path).data(), length, buffer.data(), nullptr);
45 
46  if (result == 0) {
47  return WindowsError("Failed to determine fullpath");
48  }
49 
50  return strings::remove(
51  stringify(std::wstring(buffer.data())),
52  os::LONGPATH_PREFIX,
54 }
55 
56 
57 // Gets symlink data for a given path, if it exists.
58 //
59 // This turns out to be a very complicated task on Windows. The gist of it is
60 // that we know that symlinks on Windows are implemented with the Reparse Point
61 // API, and so the process is a matter of:
62 //
63 // 1. Checking whether the attributes for the file/folder specified by the
64 // path have the reparse point bit set; all symlinks are implemented with
65 // reparse points, so this bit should be on all symlinks.
66 // 2. Opening a file/folder handle for that path, instructing it specifically
67 // to open a handle for the symlink (if the path points at a symlink) and
68 // *not* the file the symlink points at (as is the default). Note that
69 // file and folder handles are different, so we have a function that
70 // chooses appropriately.
71 // 3. Using `DeviceIoControl` to obtain information about the handle for this
72 // reparse point, which we can then query to figure out if it's a reparse
73 // point that is owned by the symlink filesystem filter driver.
74 // 4. If it is, then we report that this path does point at a symlink.
75 //
76 // NOTE: it may be helpful to consult the documentation for each of these
77 // functions, as they give you sources that justify the arguments to the
78 // obscure APIs we call to get this all working.
80 {
81  // Convert to absolute path because Windows APIs expect it.
82  const Result<std::string> absolute_path = fullpath(path);
83  if (!absolute_path.isSome()) {
84  return Error(absolute_path.error());
85  }
86 
87  // Windows has no built-in way to tell whether a path points at a symbolic
88  // link; but, we know that symbolic links are implemented with reparse
89  // points, so we begin by checking that.
90  Try<bool> is_reparse_point = reparse_point_attribute_set(
91  ::internal::windows::longpath(absolute_path.get()));
92 
93  if (is_reparse_point.isError()) {
94  return Error(is_reparse_point.error());
95  } else if (!is_reparse_point.get()) {
96  return Error(
97  "Reparse point attribute is not set for path '" + absolute_path.get() +
98  "', and therefore it is not a symbolic link");
99  }
100 
101  const Try<SharedHandle> symlink_handle =
102  get_handle_no_follow(absolute_path.get());
103 
104  if (symlink_handle.isError()) {
105  return Error(symlink_handle.error());
106  }
107 
108  // Finally, retrieve symlink data for the handle, if any.
109  return get_symbolic_link_data(symlink_handle.get().get_handle());
110 }
111 
112 } // namespace windows {
113 } // namespace internal {
114 
115 #endif // __STOUT_INTERNAL_WINDOWS_SYMLINK_HPP__
Result< std::string > fullpath(const std::string &path)
Definition: symlink.hpp:30
Definition: path.hpp:26
Try< SharedHandle > get_handle_no_follow(const std::string &absolute_path)
Definition: reparsepoint.hpp:215
Definition: errorbase.hpp:36
Definition: check.hpp:33
static Result< T > error(const std::string &message)
Definition: result.hpp:53
Definition: error.hpp:108
Try< SymbolicLink > query_symbolic_link_data(const std::string &path)
Definition: symlink.hpp:79
Try< bool > reparse_point_attribute_set(const std::wstring &absolute_path)
Definition: reparsepoint.hpp:111
Definition: check.hpp:30
std::string remove(const std::string &from, const std::string &substring, Mode mode=ANY)
Definition: strings.hpp:41
Try< SymbolicLink > get_symbolic_link_data(const HANDLE handle)
Definition: reparsepoint.hpp:271
const T & get() const
Definition: result.hpp:115
Definition: attributes.hpp:24
bool isSome() const
Definition: result.hpp:111
std::wstring longpath(const std::string &path)
Definition: longpath.hpp:38
std::string stringify(int flags)