CppCommon  1.0.4.1
C++ Common Library
symlink.cpp
Go to the documentation of this file.
1 
9 #include "filesystem/symlink.h"
10 
11 #include "utility/resource.h"
12 
13 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <unistd.h>
18 #include <vector>
19 #elif defined(_WIN32) || defined(_WIN64)
20 #include <windows.h>
21 #include <winioctl.h>
22 // REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
23 // Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
24 // here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
25 #if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)
26 #define SYMLINK_FLAG_RELATIVE 1
27 typedef struct _REPARSE_DATA_BUFFER
28 {
29  ULONG ReparseTag;
30  USHORT ReparseDataLength;
31  USHORT Reserved;
32  union
33  {
34  struct
35  {
36  USHORT SubstituteNameOffset;
37  USHORT SubstituteNameLength;
38  USHORT PrintNameOffset;
39  USHORT PrintNameLength;
40  ULONG Flags;
41  WCHAR PathBuffer[1];
42  } SymbolicLinkReparseBuffer;
43  struct
44  {
45  USHORT SubstituteNameOffset;
46  USHORT SubstituteNameLength;
47  USHORT PrintNameOffset;
48  USHORT PrintNameLength;
49  WCHAR PathBuffer[1];
50  } MountPointReparseBuffer;
51  struct
52  {
53  UCHAR DataBuffer[1];
54  } GenericReparseBuffer;
55  };
56 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
57 #define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
58 #endif
59 #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
60 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
61 #endif
62 #endif
63 
64 namespace CppCommon {
65 
67 {
68 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
69  std::vector<char> buffer(PATH_MAX);
70  ssize_t size;
71 
72  while ((size = readlink(string().c_str(), buffer.data(), buffer.size())) == (ssize_t)buffer.size())
73  buffer.resize(buffer.size() * 2);
74 
75  if (size < 0)
76  throwex FileSystemException("Cannot read symlink target of the path!").Attach(*this);
77 
78  // Return symbolic link target path
79  return Path(std::string(buffer.data(), size));
80 #elif defined(_WIN32) || defined(_WIN64)
81  HANDLE hSymlink = CreateFileW(wstring().c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
82  if (hSymlink == INVALID_HANDLE_VALUE)
83  throwex FileSystemException("Cannot open a symlink path for reading!").Attach(*this);
84 
85  // Smart resource cleaner pattern
86  auto file = resource(hSymlink, [](HANDLE hObject) { CloseHandle(hObject); });
87 
88  union info_t
89  {
90  char buffer[REPARSE_DATA_BUFFER_HEADER_SIZE + MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
91  REPARSE_DATA_BUFFER rdb;
92  };
93 
94  DWORD size;
95  info_t info;
96  if (!DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, nullptr, 0, info.buffer, sizeof(info), &size, nullptr))
97  throwex FileSystemException("Cannot read symlink target of the path!").Attach(*this);
98 
99  // Release the resource manually
100  if (!CloseHandle(hSymlink))
101  throwex FileSystemException("Cannot close a symlink!").Attach(*this);
102  file.release();
103 
104  // Get symbolic link target path
105  std::wstring result((wchar_t*)info.rdb.SymbolicLinkReparseBuffer.PathBuffer +
106  (info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)),
107  (wchar_t*)info.rdb.SymbolicLinkReparseBuffer.PathBuffer +
108  (info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)) +
109  (info.rdb.SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)));
110 
111  // Return symbolic link target path
112  return Path(Encoding::ToUTF8(result));
113 #endif
114 }
115 
117 {
118 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
119  struct stat lstatus;
120  int result = lstat(string().c_str(), &lstatus);
121  if (result != 0)
122  {
123  if ((errno == ENOENT) || (errno == ENOTDIR))
124  return false;
125  else
126  throwex FileSystemException("Cannot get the status of the symbolic link!").Attach(*this);
127  }
128 
129  if (S_ISLNK(lstatus.st_mode))
130  return true;
131  else
132  return false;
133 #elif defined(_WIN32) || defined(_WIN64)
134  DWORD attributes = GetFileAttributesW(wstring().c_str());
135  if (attributes == INVALID_FILE_ATTRIBUTES)
136  return false;
137 
138  return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
139 #endif
140 }
141 
142 Symlink Symlink::CreateSymlink(const Path& src, const Path& dst)
143 {
144 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
145  int result = symlink(src.string().c_str(), dst.string().c_str());
146  if (result != 0)
147  throwex FileSystemException("Cannot create symbolic link!").Attach(src, dst);
148 #elif defined(_WIN32) || defined(_WIN64)
149  std::wstring source = src.wstring();
150  DWORD attributes = GetFileAttributesW(source.c_str());
151  if (attributes == INVALID_FILE_ATTRIBUTES)
152  throwex FileSystemException("Cannot get file attributes of the source path!").Attach(src);
153 
154  if (attributes & FILE_ATTRIBUTE_DIRECTORY)
155  {
156  if (!CreateSymbolicLinkW(dst.wstring().c_str(), source.c_str(), SYMBOLIC_LINK_FLAG_DIRECTORY))
157  throwex FileSystemException("Cannot create symbolic link for the directory!").Attach(src, dst);
158  }
159  else
160  {
161  if (!CreateSymbolicLinkW(dst.wstring().c_str(), source.c_str(), 0))
162  throwex FileSystemException("Cannot create symbolic link for the file!").Attach(src, dst);
163  }
164 #endif
165  return dst;
166 }
167 
168 Path Symlink::CreateHardlink(const Path& src, const Path& dst)
169 {
170 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
171  int result = link(src.string().c_str(), dst.string().c_str());
172  if (result != 0)
173  throwex FileSystemException("Cannot create hard link!").Attach(src, dst);
174 #elif defined(_WIN32) || defined(_WIN64)
175  if (!CreateHardLinkW(dst.wstring().c_str(), src.wstring().c_str(), nullptr))
176  throwex FileSystemException("Cannot create hard link!").Attach(src, dst);
177 #endif
178  return dst;
179 }
180 
181 } // namespace CppCommon
static std::string ToUTF8(std::wstring_view wstr)
Convert system wide-string to UTF-8 encoded string.
Definition: encoding.cpp:19
File system exception.
Definition: exceptions.h:19
FileSystemException & Attach(const Path &path)
Attach the given path to the exception.
Definition: exceptions.h:33
Filesystem path.
Definition: path.h:90
std::wstring wstring() const
Get the path value as a wide string.
Definition: path.h:153
Flags< FileAttributes > attributes() const
Get the path file attributes.
Definition: path.cpp:472
Path()
Initialize path with an empty value.
Definition: path.h:93
const std::string & string() const noexcept
Get the path value as UTF-8 string.
Definition: path.h:151
#define throwex
Throw extended exception macro.
Definition: exceptions.h:23
C++ Common project definitions.
Definition: token_bucket.h:15
auto resource(T handle, TCleaner cleaner)
Resource smart cleaner pattern.
Definition: resource.h:43
Resource smart cleaner pattern definition.