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"
18*190fadcdSZachary Turner 
19*190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h"
20cc815568SOleksiy Vyalov #include "llvm/Support/FileSystem.h"
21c00cf4a0SZachary Turner 
22c00cf4a0SZachary Turner using namespace lldb_private;
23c00cf4a0SZachary Turner 
244eff2d31SZachary Turner const char *
254eff2d31SZachary Turner FileSystem::DEV_NULL = "nul";
264eff2d31SZachary Turner 
27*190fadcdSZachary Turner const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding";
28*190fadcdSZachary Turner 
29c00cf4a0SZachary Turner FileSpec::PathSyntax
30c00cf4a0SZachary Turner FileSystem::GetNativePathSyntax()
31c00cf4a0SZachary Turner {
32c00cf4a0SZachary Turner     return FileSpec::ePathSyntaxWindows;
33c00cf4a0SZachary Turner }
34c00cf4a0SZachary Turner 
35c00cf4a0SZachary Turner Error
36d3173f34SChaoren Lin FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
37c00cf4a0SZachary Turner {
38c00cf4a0SZachary Turner     // On Win32, the mode parameter is ignored, as Windows files and directories support a
39c00cf4a0SZachary Turner     // different permission model than POSIX.
40c00cf4a0SZachary Turner     Error error;
41d3173f34SChaoren Lin     const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
42cc815568SOleksiy Vyalov     if (err_code)
43cc815568SOleksiy Vyalov     {
44cc815568SOleksiy Vyalov         error.SetErrorString(err_code.message().c_str());
45cc815568SOleksiy Vyalov     }
46cc815568SOleksiy Vyalov 
47c00cf4a0SZachary Turner     return error;
48c00cf4a0SZachary Turner }
49c00cf4a0SZachary Turner 
50c00cf4a0SZachary Turner Error
51d3173f34SChaoren Lin FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
52c00cf4a0SZachary Turner {
53c00cf4a0SZachary Turner     Error error;
54*190fadcdSZachary Turner     std::wstring path_buffer;
55*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer))
56*190fadcdSZachary Turner     {
57*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
58*190fadcdSZachary Turner         return error;
59*190fadcdSZachary Turner     }
60c00cf4a0SZachary Turner     if (!recurse)
61c00cf4a0SZachary Turner     {
62*190fadcdSZachary Turner         BOOL result = ::RemoveDirectoryW(path_buffer.c_str());
63c00cf4a0SZachary Turner         if (!result)
64c00cf4a0SZachary Turner             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
65c00cf4a0SZachary Turner     }
66c00cf4a0SZachary Turner     else
67c00cf4a0SZachary Turner     {
68c00cf4a0SZachary Turner         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
69*190fadcdSZachary Turner         // indicate the end of the list. The first null terminator is there only in the backing
70*190fadcdSZachary Turner         // store but not the actual vector contents, and so we need to push twice.
71*190fadcdSZachary Turner         path_buffer.push_back(0);
72c00cf4a0SZachary Turner         path_buffer.push_back(0);
73c00cf4a0SZachary Turner 
74*190fadcdSZachary Turner         SHFILEOPSTRUCTW shfos = {0};
75c00cf4a0SZachary Turner         shfos.wFunc = FO_DELETE;
76*190fadcdSZachary Turner         shfos.pFrom = (LPCWSTR)path_buffer.data();
77c00cf4a0SZachary Turner         shfos.fFlags = FOF_NO_UI;
78c00cf4a0SZachary Turner 
79*190fadcdSZachary Turner         int result = ::SHFileOperationW(&shfos);
80c00cf4a0SZachary Turner         // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
81c00cf4a0SZachary Turner         if (result != 0)
82c00cf4a0SZachary Turner             error.SetErrorStringWithFormat("SHFileOperation failed");
83c00cf4a0SZachary Turner     }
84c00cf4a0SZachary Turner     return error;
85c00cf4a0SZachary Turner }
86c00cf4a0SZachary Turner 
87c00cf4a0SZachary Turner Error
88d3173f34SChaoren Lin FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
89c00cf4a0SZachary Turner {
90c00cf4a0SZachary Turner     Error error;
91633594c2SAdrian McCarthy     // Beware that Windows's permission model is different from Unix's, and it's
92633594c2SAdrian McCarthy     // not clear if this API is supposed to check ACLs.  To match the caller's
93633594c2SAdrian McCarthy     // expectations as closely as possible, we'll use Microsoft's _stat, which
94633594c2SAdrian McCarthy     // attempts to emulate POSIX stat.  This should be good enough for basic
95633594c2SAdrian McCarthy     // checks like FileSpec::Readable.
96633594c2SAdrian McCarthy     struct _stat file_stats;
97633594c2SAdrian McCarthy     if (::_stat(file_spec.GetCString(), &file_stats) == 0)
98633594c2SAdrian McCarthy     {
99633594c2SAdrian McCarthy         // The owner permission bits in "st_mode" currently match the definitions
100633594c2SAdrian McCarthy         // for the owner file mode bits.
101633594c2SAdrian McCarthy         file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
102633594c2SAdrian McCarthy     }
103633594c2SAdrian McCarthy     else
104633594c2SAdrian McCarthy     {
105633594c2SAdrian McCarthy         error.SetErrorToErrno();
106633594c2SAdrian McCarthy     }
107633594c2SAdrian McCarthy 
108c00cf4a0SZachary Turner     return error;
109c00cf4a0SZachary Turner }
110c00cf4a0SZachary Turner 
111c00cf4a0SZachary Turner Error
112d3173f34SChaoren Lin FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
113c00cf4a0SZachary Turner {
114c00cf4a0SZachary Turner     Error error;
115c00cf4a0SZachary Turner     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
116c00cf4a0SZachary Turner     return error;
117c00cf4a0SZachary Turner }
118c00cf4a0SZachary Turner 
119c00cf4a0SZachary Turner lldb::user_id_t
120c00cf4a0SZachary Turner FileSystem::GetFileSize(const FileSpec &file_spec)
121c00cf4a0SZachary Turner {
122c00cf4a0SZachary Turner     return file_spec.GetByteSize();
123c00cf4a0SZachary Turner }
124c00cf4a0SZachary Turner 
125c00cf4a0SZachary Turner bool
126c00cf4a0SZachary Turner FileSystem::GetFileExists(const FileSpec &file_spec)
127c00cf4a0SZachary Turner {
128c00cf4a0SZachary Turner     return file_spec.Exists();
129c00cf4a0SZachary Turner }
130c00cf4a0SZachary Turner 
131c00cf4a0SZachary Turner Error
132d3173f34SChaoren Lin FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
133a9ea0711SOleksiy Vyalov {
134a9ea0711SOleksiy Vyalov     Error error;
135*190fadcdSZachary Turner     std::wstring wsrc, wdst;
136*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
137*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
138*190fadcdSZachary Turner     else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr))
139a9ea0711SOleksiy Vyalov         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
140a9ea0711SOleksiy Vyalov     return error;
141a9ea0711SOleksiy Vyalov }
142a9ea0711SOleksiy Vyalov 
1432b38f33bSOleksiy Vyalov int
1442b38f33bSOleksiy Vyalov FileSystem::GetHardlinkCount(const FileSpec &file_spec)
1452b38f33bSOleksiy Vyalov {
146*190fadcdSZachary Turner     std::wstring path;
147*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
148*190fadcdSZachary Turner         return -1;
149*190fadcdSZachary Turner 
150*190fadcdSZachary Turner     HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
151*190fadcdSZachary Turner                                        FILE_ATTRIBUTE_NORMAL, nullptr);
1522b38f33bSOleksiy Vyalov 
1532b38f33bSOleksiy Vyalov     if (file_handle == INVALID_HANDLE_VALUE)
1542b38f33bSOleksiy Vyalov       return -1;
1552b38f33bSOleksiy Vyalov 
1562b38f33bSOleksiy Vyalov     AutoHandle auto_file_handle(file_handle);
1572b38f33bSOleksiy Vyalov     BY_HANDLE_FILE_INFORMATION file_info;
1582b38f33bSOleksiy Vyalov     if (::GetFileInformationByHandle(file_handle, &file_info))
1592b38f33bSOleksiy Vyalov         return file_info.nNumberOfLinks;
1602b38f33bSOleksiy Vyalov 
1612b38f33bSOleksiy Vyalov     return -1;
1622b38f33bSOleksiy Vyalov }
1632b38f33bSOleksiy Vyalov 
164a9ea0711SOleksiy Vyalov Error
165d3173f34SChaoren Lin FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
166c00cf4a0SZachary Turner {
167c00cf4a0SZachary Turner     Error error;
168*190fadcdSZachary Turner     std::wstring wsrc, wdst;
169*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
170*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
171*190fadcdSZachary Turner     if (error.Fail())
172*190fadcdSZachary Turner         return error;
173*190fadcdSZachary Turner     DWORD attrib = ::GetFileAttributesW(wdst.c_str());
174c00cf4a0SZachary Turner     if (attrib == INVALID_FILE_ATTRIBUTES)
175c00cf4a0SZachary Turner     {
176c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
177c00cf4a0SZachary Turner         return error;
178c00cf4a0SZachary Turner     }
179c00cf4a0SZachary Turner     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
180c00cf4a0SZachary Turner     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
181*190fadcdSZachary Turner     BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag);
182c00cf4a0SZachary Turner     if (!result)
183c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
184c00cf4a0SZachary Turner     return error;
185c00cf4a0SZachary Turner }
186c00cf4a0SZachary Turner 
187c00cf4a0SZachary Turner Error
188d3173f34SChaoren Lin FileSystem::Unlink(const FileSpec &file_spec)
189c00cf4a0SZachary Turner {
190c00cf4a0SZachary Turner     Error error;
191*190fadcdSZachary Turner     std::wstring path;
192*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
193*190fadcdSZachary Turner     {
194*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
195*190fadcdSZachary Turner         return error;
196*190fadcdSZachary Turner     }
197*190fadcdSZachary Turner     BOOL result = ::DeleteFileW(path.c_str());
198c00cf4a0SZachary Turner     if (!result)
199c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
200c00cf4a0SZachary Turner     return error;
201c00cf4a0SZachary Turner }
202c00cf4a0SZachary Turner 
203c00cf4a0SZachary Turner Error
204d3173f34SChaoren Lin FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
205c00cf4a0SZachary Turner {
206c00cf4a0SZachary Turner     Error error;
207*190fadcdSZachary Turner     std::wstring wsrc;
208*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc))
209*190fadcdSZachary Turner     {
210*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
211*190fadcdSZachary Turner         return error;
212*190fadcdSZachary Turner     }
213*190fadcdSZachary Turner 
214*190fadcdSZachary Turner     HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
215c00cf4a0SZachary Turner                              FILE_FLAG_OPEN_REPARSE_POINT, NULL);
216c00cf4a0SZachary Turner     if (h == INVALID_HANDLE_VALUE)
217c00cf4a0SZachary Turner     {
218c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
219c00cf4a0SZachary Turner         return error;
220c00cf4a0SZachary Turner     }
221c00cf4a0SZachary Turner 
222*190fadcdSZachary Turner     std::vector<wchar_t> buf(PATH_MAX + 1);
223c00cf4a0SZachary Turner     // Subtract 1 from the path length since this function does not add a null terminator.
224*190fadcdSZachary Turner     DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
225*190fadcdSZachary Turner     std::string path;
226c00cf4a0SZachary Turner     if (result == 0)
227c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
228*190fadcdSZachary Turner     else if (!llvm::convertWideToUTF8(buf.data(), path))
229*190fadcdSZachary Turner         error.SetErrorString(PATH_CONVERSION_ERROR);
230d3173f34SChaoren Lin     else
231*190fadcdSZachary Turner         dst.SetFile(path, false);
232c00cf4a0SZachary Turner 
233c00cf4a0SZachary Turner     ::CloseHandle(h);
234c00cf4a0SZachary Turner     return error;
235c00cf4a0SZachary Turner }
236736888c8SGreg Clayton 
2379077e9f8SSean Callanan Error
2389077e9f8SSean Callanan FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst)
2399077e9f8SSean Callanan {
2409077e9f8SSean Callanan     return Error("ResolveSymbolicLink() isn't implemented on Windows");
2419077e9f8SSean Callanan }
2429077e9f8SSean Callanan 
243736888c8SGreg Clayton bool
244736888c8SGreg Clayton FileSystem::IsLocal(const FileSpec &spec)
245736888c8SGreg Clayton {
246736888c8SGreg Clayton     if (spec)
247736888c8SGreg Clayton     {
248736888c8SGreg Clayton         // TODO: return true if the file is on a locally mounted file system
249736888c8SGreg Clayton         return true;
250736888c8SGreg Clayton     }
251736888c8SGreg Clayton 
252736888c8SGreg Clayton     return false;
253736888c8SGreg Clayton }
254*190fadcdSZachary Turner 
255*190fadcdSZachary Turner FILE *
256*190fadcdSZachary Turner FileSystem::Fopen(const char *path, const char *mode)
257*190fadcdSZachary Turner {
258*190fadcdSZachary Turner     std::wstring wpath, wmode;
259*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(path, wpath))
260*190fadcdSZachary Turner         return nullptr;
261*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(mode, wmode))
262*190fadcdSZachary Turner         return nullptr;
263*190fadcdSZachary Turner     FILE *file;
264*190fadcdSZachary Turner     if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0)
265*190fadcdSZachary Turner         return nullptr;
266*190fadcdSZachary Turner     return file;
267*190fadcdSZachary Turner }
268*190fadcdSZachary Turner 
269*190fadcdSZachary Turner int
270*190fadcdSZachary Turner FileSystem::Stat(const char *path, struct stat *stats)
271*190fadcdSZachary Turner {
272*190fadcdSZachary Turner     std::wstring wpath;
273*190fadcdSZachary Turner     if (!llvm::ConvertUTF8toWide(path, wpath))
274*190fadcdSZachary Turner     {
275*190fadcdSZachary Turner         errno = EINVAL;
276*190fadcdSZachary Turner         return -EINVAL;
277*190fadcdSZachary Turner     }
278*190fadcdSZachary Turner     int stat_result;
279*190fadcdSZachary Turner #ifdef _USE_32BIT_TIME_T
280*190fadcdSZachary Turner     struct _stat32 file_stats;
281*190fadcdSZachary Turner     stat_result = ::_wstat32(wpath.c_str(), &file_stats);
282*190fadcdSZachary Turner #else
283*190fadcdSZachary Turner     struct _stat64i32 file_stats;
284*190fadcdSZachary Turner     stat_result = ::_wstat64i32(wpath.c_str(), &file_stats);
285*190fadcdSZachary Turner #endif
286*190fadcdSZachary Turner     if (stat_result == 0)
287*190fadcdSZachary Turner     {
288*190fadcdSZachary Turner         static_assert(sizeof(struct stat) == sizeof(file_stats),
289*190fadcdSZachary Turner                       "stat and _stat32/_stat64i32 must have the same layout");
290*190fadcdSZachary Turner         *stats = *reinterpret_cast<struct stat *>(&file_stats);
291*190fadcdSZachary Turner     }
292*190fadcdSZachary Turner     return stat_result;
293*190fadcdSZachary Turner }
294