CppCommon 1.0.5.0
C++ Common Library
Loading...
Searching...
No Matches
symlink.cpp
Go to the documentation of this file.
1
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
27typedef 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
64namespace 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
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
168Path 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:24
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.
auto resource(T handle, TCleaner cleaner)
Resource smart cleaner pattern.
Definition resource.h:43
Resource smart cleaner pattern definition.