]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - source/Host/windows/FileSystem.cpp
Vendor import of lldb release_39 branch r276489:
[FreeBSD/FreeBSD.git] / source / Host / windows / FileSystem.cpp
1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/Host/windows/windows.h"
11
12 #include <shellapi.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15
16 #include "lldb/Host/FileSystem.h"
17 #include "lldb/Host/windows/AutoHandle.h"
18
19 #include "llvm/Support/ConvertUTF.h"
20 #include "llvm/Support/FileSystem.h"
21
22 using namespace lldb_private;
23
24 const char *
25 FileSystem::DEV_NULL = "nul";
26
27 const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding";
28
29 FileSpec::PathSyntax
30 FileSystem::GetNativePathSyntax()
31 {
32     return FileSpec::ePathSyntaxWindows;
33 }
34
35 Error
36 FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
37 {
38     // On Win32, the mode parameter is ignored, as Windows files and directories support a
39     // different permission model than POSIX.
40     Error error;
41     const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
42     if (err_code)
43     {
44         error.SetErrorString(err_code.message().c_str());
45     }
46
47     return error;
48 }
49
50 Error
51 FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
52 {
53     Error error;
54     std::wstring path_buffer;
55     if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer))
56     {
57         error.SetErrorString(PATH_CONVERSION_ERROR);
58         return error;
59     }
60     if (!recurse)
61     {
62         BOOL result = ::RemoveDirectoryW(path_buffer.c_str());
63         if (!result)
64             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
65     }
66     else
67     {
68         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
69         // indicate the end of the list. The first null terminator is there only in the backing
70         // store but not the actual vector contents, and so we need to push twice.
71         path_buffer.push_back(0);
72         path_buffer.push_back(0);
73
74         SHFILEOPSTRUCTW shfos = {0};
75         shfos.wFunc = FO_DELETE;
76         shfos.pFrom = (LPCWSTR)path_buffer.data();
77         shfos.fFlags = FOF_NO_UI;
78
79         int result = ::SHFileOperationW(&shfos);
80         // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
81         if (result != 0)
82             error.SetErrorStringWithFormat("SHFileOperation failed");
83     }
84     return error;
85 }
86
87 Error
88 FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
89 {
90     Error error;
91     // Beware that Windows's permission model is different from Unix's, and it's
92     // not clear if this API is supposed to check ACLs.  To match the caller's
93     // expectations as closely as possible, we'll use Microsoft's _stat, which
94     // attempts to emulate POSIX stat.  This should be good enough for basic
95     // checks like FileSpec::Readable.
96     struct _stat file_stats;
97     if (::_stat(file_spec.GetCString(), &file_stats) == 0)
98     {
99         // The owner permission bits in "st_mode" currently match the definitions
100         // for the owner file mode bits.
101         file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
102     }
103     else
104     {
105         error.SetErrorToErrno();
106     }
107
108     return error;
109 }
110
111 Error
112 FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
113 {
114     Error error;
115     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
116     return error;
117 }
118
119 lldb::user_id_t
120 FileSystem::GetFileSize(const FileSpec &file_spec)
121 {
122     return file_spec.GetByteSize();
123 }
124
125 bool
126 FileSystem::GetFileExists(const FileSpec &file_spec)
127 {
128     return file_spec.Exists();
129 }
130
131 Error
132 FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
133 {
134     Error error;
135     std::wstring wsrc, wdst;
136     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
137         error.SetErrorString(PATH_CONVERSION_ERROR);
138     else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr))
139         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
140     return error;
141 }
142
143 int
144 FileSystem::GetHardlinkCount(const FileSpec &file_spec)
145 {
146     std::wstring path;
147     if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
148         return -1;
149
150     HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
151                                        FILE_ATTRIBUTE_NORMAL, nullptr);
152
153     if (file_handle == INVALID_HANDLE_VALUE)
154       return -1;
155
156     AutoHandle auto_file_handle(file_handle);
157     BY_HANDLE_FILE_INFORMATION file_info;
158     if (::GetFileInformationByHandle(file_handle, &file_info))
159         return file_info.nNumberOfLinks;
160
161     return -1;
162 }
163
164 Error
165 FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
166 {
167     Error error;
168     std::wstring wsrc, wdst;
169     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
170         error.SetErrorString(PATH_CONVERSION_ERROR);
171     if (error.Fail())
172         return error;
173     DWORD attrib = ::GetFileAttributesW(wdst.c_str());
174     if (attrib == INVALID_FILE_ATTRIBUTES)
175     {
176         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
177         return error;
178     }
179     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
180     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
181     BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag);
182     if (!result)
183         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
184     return error;
185 }
186
187 Error
188 FileSystem::Unlink(const FileSpec &file_spec)
189 {
190     Error error;
191     std::wstring path;
192     if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
193     {
194         error.SetErrorString(PATH_CONVERSION_ERROR);
195         return error;
196     }
197     BOOL result = ::DeleteFileW(path.c_str());
198     if (!result)
199         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
200     return error;
201 }
202
203 Error
204 FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
205 {
206     Error error;
207     std::wstring wsrc;
208     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc))
209     {
210         error.SetErrorString(PATH_CONVERSION_ERROR);
211         return error;
212     }
213
214     HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
215                              FILE_FLAG_OPEN_REPARSE_POINT, NULL);
216     if (h == INVALID_HANDLE_VALUE)
217     {
218         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
219         return error;
220     }
221
222     std::vector<wchar_t> buf(PATH_MAX + 1);
223     // Subtract 1 from the path length since this function does not add a null terminator.
224     DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
225     std::string path;
226     if (result == 0)
227         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
228     else if (!llvm::convertWideToUTF8(buf.data(), path))
229         error.SetErrorString(PATH_CONVERSION_ERROR);
230     else
231         dst.SetFile(path, false);
232
233     ::CloseHandle(h);
234     return error;
235 }
236
237 Error
238 FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst)
239 {
240     return Error("ResolveSymbolicLink() isn't implemented on Windows");
241 }
242
243 bool
244 FileSystem::IsLocal(const FileSpec &spec)
245 {
246     if (spec)
247     {
248         // TODO: return true if the file is on a locally mounted file system
249         return true;
250     }
251
252     return false;
253 }
254
255 FILE *
256 FileSystem::Fopen(const char *path, const char *mode)
257 {
258     std::wstring wpath, wmode;
259     if (!llvm::ConvertUTF8toWide(path, wpath))
260         return nullptr;
261     if (!llvm::ConvertUTF8toWide(mode, wmode))
262         return nullptr;
263     FILE *file;
264     if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0)
265         return nullptr;
266     return file;
267 }
268
269 int
270 FileSystem::Stat(const char *path, struct stat *stats)
271 {
272     std::wstring wpath;
273     if (!llvm::ConvertUTF8toWide(path, wpath))
274     {
275         errno = EINVAL;
276         return -EINVAL;
277     }
278     int stat_result;
279 #ifdef _USE_32BIT_TIME_T
280     struct _stat32 file_stats;
281     stat_result = ::_wstat32(wpath.c_str(), &file_stats);
282 #else
283     struct _stat64i32 file_stats;
284     stat_result = ::_wstat64i32(wpath.c_str(), &file_stats);
285 #endif
286     if (stat_result == 0)
287     {
288         static_assert(sizeof(struct stat) == sizeof(file_stats),
289                       "stat and _stat32/_stat64i32 must have the same layout");
290         *stats = *reinterpret_cast<struct stat *>(&file_stats);
291     }
292     return stat_result;
293 }