1c00cf4a0SZachary Turner //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
2c00cf4a0SZachary Turner //
3c00cf4a0SZachary Turner //                     The LLVM Compiler Infrastructure
4c00cf4a0SZachary Turner //
5c00cf4a0SZachary Turner // This file is distributed under the University of Illinois Open Source
6c00cf4a0SZachary Turner // License. See LICENSE.TXT for details.
7c00cf4a0SZachary Turner //
8c00cf4a0SZachary Turner //===----------------------------------------------------------------------===//
9c00cf4a0SZachary Turner 
10c00cf4a0SZachary Turner #include "lldb/Host/windows/windows.h"
11c00cf4a0SZachary Turner 
12c00cf4a0SZachary Turner #include <shellapi.h>
13633594c2SAdrian McCarthy #include <sys/stat.h>
14633594c2SAdrian McCarthy #include <sys/types.h>
15c00cf4a0SZachary Turner 
16c00cf4a0SZachary Turner #include "lldb/Host/FileSystem.h"
172b38f33bSOleksiy Vyalov #include "lldb/Host/windows/AutoHandle.h"
18190fadcdSZachary Turner 
19190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h"
20cc815568SOleksiy Vyalov #include "llvm/Support/FileSystem.h"
21c00cf4a0SZachary Turner 
22c00cf4a0SZachary Turner using namespace lldb_private;
23c00cf4a0SZachary Turner 
24*b9c1b51eSKate Stone const char *FileSystem::DEV_NULL = "nul";
254eff2d31SZachary Turner 
26*b9c1b51eSKate Stone const char *FileSystem::PATH_CONVERSION_ERROR =
27*b9c1b51eSKate Stone     "Error converting path between UTF-8 and native encoding";
28190fadcdSZachary Turner 
29*b9c1b51eSKate Stone FileSpec::PathSyntax FileSystem::GetNativePathSyntax() {
30c00cf4a0SZachary Turner   return FileSpec::ePathSyntaxWindows;
31c00cf4a0SZachary Turner }
32c00cf4a0SZachary Turner 
33*b9c1b51eSKate Stone Error FileSystem::MakeDirectory(const FileSpec &file_spec,
34*b9c1b51eSKate Stone                                 uint32_t file_permissions) {
35*b9c1b51eSKate Stone   // On Win32, the mode parameter is ignored, as Windows files and directories
36*b9c1b51eSKate Stone   // support a
37c00cf4a0SZachary Turner   // different permission model than POSIX.
38c00cf4a0SZachary Turner   Error error;
39*b9c1b51eSKate Stone   const auto err_code =
40*b9c1b51eSKate Stone       llvm::sys::fs::create_directories(file_spec.GetPath(), true);
41*b9c1b51eSKate Stone   if (err_code) {
42cc815568SOleksiy Vyalov     error.SetErrorString(err_code.message().c_str());
43cc815568SOleksiy Vyalov   }
44cc815568SOleksiy Vyalov 
45c00cf4a0SZachary Turner   return error;
46c00cf4a0SZachary Turner }
47c00cf4a0SZachary Turner 
48*b9c1b51eSKate Stone Error FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) {
49c00cf4a0SZachary Turner   Error error;
50190fadcdSZachary Turner   std::wstring path_buffer;
51*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) {
52190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
53190fadcdSZachary Turner     return error;
54190fadcdSZachary Turner   }
55*b9c1b51eSKate Stone   if (!recurse) {
56190fadcdSZachary Turner     BOOL result = ::RemoveDirectoryW(path_buffer.c_str());
57c00cf4a0SZachary Turner     if (!result)
58c00cf4a0SZachary Turner       error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
59*b9c1b51eSKate Stone   } else {
60*b9c1b51eSKate Stone     // SHFileOperation() accepts a list of paths, and so must be
61*b9c1b51eSKate Stone     // double-null-terminated to
62*b9c1b51eSKate Stone     // indicate the end of the list. The first null terminator is there only in
63*b9c1b51eSKate Stone     // the backing
64190fadcdSZachary Turner     // store but not the actual vector contents, and so we need to push twice.
65190fadcdSZachary Turner     path_buffer.push_back(0);
66c00cf4a0SZachary Turner     path_buffer.push_back(0);
67c00cf4a0SZachary Turner 
68190fadcdSZachary Turner     SHFILEOPSTRUCTW shfos = {0};
69c00cf4a0SZachary Turner     shfos.wFunc = FO_DELETE;
70190fadcdSZachary Turner     shfos.pFrom = (LPCWSTR)path_buffer.data();
71c00cf4a0SZachary Turner     shfos.fFlags = FOF_NO_UI;
72c00cf4a0SZachary Turner 
73190fadcdSZachary Turner     int result = ::SHFileOperationW(&shfos);
74*b9c1b51eSKate Stone     // TODO(zturner): Correctly handle the intricacies of SHFileOperation return
75*b9c1b51eSKate Stone     // values.
76c00cf4a0SZachary Turner     if (result != 0)
77c00cf4a0SZachary Turner       error.SetErrorStringWithFormat("SHFileOperation failed");
78c00cf4a0SZachary Turner   }
79c00cf4a0SZachary Turner   return error;
80c00cf4a0SZachary Turner }
81c00cf4a0SZachary Turner 
82*b9c1b51eSKate Stone Error FileSystem::GetFilePermissions(const FileSpec &file_spec,
83*b9c1b51eSKate Stone                                      uint32_t &file_permissions) {
84c00cf4a0SZachary Turner   Error error;
85633594c2SAdrian McCarthy   // Beware that Windows's permission model is different from Unix's, and it's
86633594c2SAdrian McCarthy   // not clear if this API is supposed to check ACLs.  To match the caller's
87633594c2SAdrian McCarthy   // expectations as closely as possible, we'll use Microsoft's _stat, which
88633594c2SAdrian McCarthy   // attempts to emulate POSIX stat.  This should be good enough for basic
89633594c2SAdrian McCarthy   // checks like FileSpec::Readable.
90633594c2SAdrian McCarthy   struct _stat file_stats;
91*b9c1b51eSKate Stone   if (::_stat(file_spec.GetCString(), &file_stats) == 0) {
92633594c2SAdrian McCarthy     // The owner permission bits in "st_mode" currently match the definitions
93633594c2SAdrian McCarthy     // for the owner file mode bits.
94633594c2SAdrian McCarthy     file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
95*b9c1b51eSKate Stone   } else {
96633594c2SAdrian McCarthy     error.SetErrorToErrno();
97633594c2SAdrian McCarthy   }
98633594c2SAdrian McCarthy 
99c00cf4a0SZachary Turner   return error;
100c00cf4a0SZachary Turner }
101c00cf4a0SZachary Turner 
102*b9c1b51eSKate Stone Error FileSystem::SetFilePermissions(const FileSpec &file_spec,
103*b9c1b51eSKate Stone                                      uint32_t file_permissions) {
104c00cf4a0SZachary Turner   Error error;
105*b9c1b51eSKate Stone   error.SetErrorStringWithFormat("%s is not supported on this host",
106*b9c1b51eSKate Stone                                  LLVM_PRETTY_FUNCTION);
107c00cf4a0SZachary Turner   return error;
108c00cf4a0SZachary Turner }
109c00cf4a0SZachary Turner 
110*b9c1b51eSKate Stone lldb::user_id_t FileSystem::GetFileSize(const FileSpec &file_spec) {
111c00cf4a0SZachary Turner   return file_spec.GetByteSize();
112c00cf4a0SZachary Turner }
113c00cf4a0SZachary Turner 
114*b9c1b51eSKate Stone bool FileSystem::GetFileExists(const FileSpec &file_spec) {
115c00cf4a0SZachary Turner   return file_spec.Exists();
116c00cf4a0SZachary Turner }
117c00cf4a0SZachary Turner 
118*b9c1b51eSKate Stone Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) {
119a9ea0711SOleksiy Vyalov   Error error;
120190fadcdSZachary Turner   std::wstring wsrc, wdst;
121*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) ||
122*b9c1b51eSKate Stone       !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
123190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
124190fadcdSZachary Turner   else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr))
125a9ea0711SOleksiy Vyalov     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
126a9ea0711SOleksiy Vyalov   return error;
127a9ea0711SOleksiy Vyalov }
128a9ea0711SOleksiy Vyalov 
129*b9c1b51eSKate Stone int FileSystem::GetHardlinkCount(const FileSpec &file_spec) {
130190fadcdSZachary Turner   std::wstring path;
131190fadcdSZachary Turner   if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
132190fadcdSZachary Turner     return -1;
133190fadcdSZachary Turner 
134*b9c1b51eSKate Stone   HANDLE file_handle =
135*b9c1b51eSKate Stone       ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
136*b9c1b51eSKate Stone                     nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
1372b38f33bSOleksiy Vyalov 
1382b38f33bSOleksiy Vyalov   if (file_handle == INVALID_HANDLE_VALUE)
1392b38f33bSOleksiy Vyalov     return -1;
1402b38f33bSOleksiy Vyalov 
1412b38f33bSOleksiy Vyalov   AutoHandle auto_file_handle(file_handle);
1422b38f33bSOleksiy Vyalov   BY_HANDLE_FILE_INFORMATION file_info;
1432b38f33bSOleksiy Vyalov   if (::GetFileInformationByHandle(file_handle, &file_info))
1442b38f33bSOleksiy Vyalov     return file_info.nNumberOfLinks;
1452b38f33bSOleksiy Vyalov 
1462b38f33bSOleksiy Vyalov   return -1;
1472b38f33bSOleksiy Vyalov }
1482b38f33bSOleksiy Vyalov 
149*b9c1b51eSKate Stone Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) {
150c00cf4a0SZachary Turner   Error error;
151190fadcdSZachary Turner   std::wstring wsrc, wdst;
152*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) ||
153*b9c1b51eSKate Stone       !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
154190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
155190fadcdSZachary Turner   if (error.Fail())
156190fadcdSZachary Turner     return error;
157190fadcdSZachary Turner   DWORD attrib = ::GetFileAttributesW(wdst.c_str());
158*b9c1b51eSKate Stone   if (attrib == INVALID_FILE_ATTRIBUTES) {
159c00cf4a0SZachary Turner     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
160c00cf4a0SZachary Turner     return error;
161c00cf4a0SZachary Turner   }
162c00cf4a0SZachary Turner   bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
163c00cf4a0SZachary Turner   DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
164190fadcdSZachary Turner   BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag);
165c00cf4a0SZachary Turner   if (!result)
166c00cf4a0SZachary Turner     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
167c00cf4a0SZachary Turner   return error;
168c00cf4a0SZachary Turner }
169c00cf4a0SZachary Turner 
170*b9c1b51eSKate Stone Error FileSystem::Unlink(const FileSpec &file_spec) {
171c00cf4a0SZachary Turner   Error error;
172190fadcdSZachary Turner   std::wstring path;
173*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) {
174190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
175190fadcdSZachary Turner     return error;
176190fadcdSZachary Turner   }
177190fadcdSZachary Turner   BOOL result = ::DeleteFileW(path.c_str());
178c00cf4a0SZachary Turner   if (!result)
179c00cf4a0SZachary Turner     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
180c00cf4a0SZachary Turner   return error;
181c00cf4a0SZachary Turner }
182c00cf4a0SZachary Turner 
183*b9c1b51eSKate Stone Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) {
184c00cf4a0SZachary Turner   Error error;
185190fadcdSZachary Turner   std::wstring wsrc;
186*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) {
187190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
188190fadcdSZachary Turner     return error;
189190fadcdSZachary Turner   }
190190fadcdSZachary Turner 
191*b9c1b51eSKate Stone   HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ,
192*b9c1b51eSKate Stone                            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
193*b9c1b51eSKate Stone                            OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);
194*b9c1b51eSKate Stone   if (h == INVALID_HANDLE_VALUE) {
195c00cf4a0SZachary Turner     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
196c00cf4a0SZachary Turner     return error;
197c00cf4a0SZachary Turner   }
198c00cf4a0SZachary Turner 
199190fadcdSZachary Turner   std::vector<wchar_t> buf(PATH_MAX + 1);
200*b9c1b51eSKate Stone   // Subtract 1 from the path length since this function does not add a null
201*b9c1b51eSKate Stone   // terminator.
202*b9c1b51eSKate Stone   DWORD result = ::GetFinalPathNameByHandleW(
203*b9c1b51eSKate Stone       h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
204190fadcdSZachary Turner   std::string path;
205c00cf4a0SZachary Turner   if (result == 0)
206c00cf4a0SZachary Turner     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
207190fadcdSZachary Turner   else if (!llvm::convertWideToUTF8(buf.data(), path))
208190fadcdSZachary Turner     error.SetErrorString(PATH_CONVERSION_ERROR);
209d3173f34SChaoren Lin   else
210190fadcdSZachary Turner     dst.SetFile(path, false);
211c00cf4a0SZachary Turner 
212c00cf4a0SZachary Turner   ::CloseHandle(h);
213c00cf4a0SZachary Turner   return error;
214c00cf4a0SZachary Turner }
215736888c8SGreg Clayton 
216*b9c1b51eSKate Stone Error FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) {
2179077e9f8SSean Callanan   return Error("ResolveSymbolicLink() isn't implemented on Windows");
2189077e9f8SSean Callanan }
2199077e9f8SSean Callanan 
220*b9c1b51eSKate Stone bool FileSystem::IsLocal(const FileSpec &spec) {
221*b9c1b51eSKate Stone   if (spec) {
222736888c8SGreg Clayton     // TODO: return true if the file is on a locally mounted file system
223736888c8SGreg Clayton     return true;
224736888c8SGreg Clayton   }
225736888c8SGreg Clayton 
226736888c8SGreg Clayton   return false;
227736888c8SGreg Clayton }
228190fadcdSZachary Turner 
229*b9c1b51eSKate Stone FILE *FileSystem::Fopen(const char *path, const char *mode) {
230190fadcdSZachary Turner   std::wstring wpath, wmode;
231190fadcdSZachary Turner   if (!llvm::ConvertUTF8toWide(path, wpath))
232190fadcdSZachary Turner     return nullptr;
233190fadcdSZachary Turner   if (!llvm::ConvertUTF8toWide(mode, wmode))
234190fadcdSZachary Turner     return nullptr;
235190fadcdSZachary Turner   FILE *file;
236190fadcdSZachary Turner   if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0)
237190fadcdSZachary Turner     return nullptr;
238190fadcdSZachary Turner   return file;
239190fadcdSZachary Turner }
240190fadcdSZachary Turner 
241*b9c1b51eSKate Stone int FileSystem::Stat(const char *path, struct stat *stats) {
242190fadcdSZachary Turner   std::wstring wpath;
243*b9c1b51eSKate Stone   if (!llvm::ConvertUTF8toWide(path, wpath)) {
244190fadcdSZachary Turner     errno = EINVAL;
245190fadcdSZachary Turner     return -EINVAL;
246190fadcdSZachary Turner   }
247190fadcdSZachary Turner   int stat_result;
248190fadcdSZachary Turner #ifdef _USE_32BIT_TIME_T
249190fadcdSZachary Turner   struct _stat32 file_stats;
250190fadcdSZachary Turner   stat_result = ::_wstat32(wpath.c_str(), &file_stats);
251190fadcdSZachary Turner #else
252190fadcdSZachary Turner   struct _stat64i32 file_stats;
253190fadcdSZachary Turner   stat_result = ::_wstat64i32(wpath.c_str(), &file_stats);
254190fadcdSZachary Turner #endif
255*b9c1b51eSKate Stone   if (stat_result == 0) {
256190fadcdSZachary Turner     static_assert(sizeof(struct stat) == sizeof(file_stats),
257190fadcdSZachary Turner                   "stat and _stat32/_stat64i32 must have the same layout");
258190fadcdSZachary Turner     *stats = *reinterpret_cast<struct stat *>(&file_stats);
259190fadcdSZachary Turner   }
260190fadcdSZachary Turner   return stat_result;
261190fadcdSZachary Turner }
262