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