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"
18cc815568SOleksiy Vyalov #include "llvm/Support/FileSystem.h"
19c00cf4a0SZachary Turner 
20c00cf4a0SZachary Turner using namespace lldb_private;
21c00cf4a0SZachary Turner 
22*4eff2d31SZachary Turner const char *
23*4eff2d31SZachary Turner FileSystem::DEV_NULL = "nul";
24*4eff2d31SZachary Turner 
25c00cf4a0SZachary Turner FileSpec::PathSyntax
26c00cf4a0SZachary Turner FileSystem::GetNativePathSyntax()
27c00cf4a0SZachary Turner {
28c00cf4a0SZachary Turner     return FileSpec::ePathSyntaxWindows;
29c00cf4a0SZachary Turner }
30c00cf4a0SZachary Turner 
31c00cf4a0SZachary Turner Error
32d3173f34SChaoren Lin FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
33c00cf4a0SZachary Turner {
34c00cf4a0SZachary Turner     // On Win32, the mode parameter is ignored, as Windows files and directories support a
35c00cf4a0SZachary Turner     // different permission model than POSIX.
36c00cf4a0SZachary Turner     Error error;
37d3173f34SChaoren Lin     const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
38cc815568SOleksiy Vyalov     if (err_code)
39cc815568SOleksiy Vyalov     {
40cc815568SOleksiy Vyalov         error.SetErrorString(err_code.message().c_str());
41cc815568SOleksiy Vyalov     }
42cc815568SOleksiy Vyalov 
43c00cf4a0SZachary Turner     return error;
44c00cf4a0SZachary Turner }
45c00cf4a0SZachary Turner 
46c00cf4a0SZachary Turner Error
47d3173f34SChaoren Lin FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
48c00cf4a0SZachary Turner {
49c00cf4a0SZachary Turner     Error error;
50c00cf4a0SZachary Turner     if (!recurse)
51c00cf4a0SZachary Turner     {
52d3173f34SChaoren Lin         BOOL result = ::RemoveDirectory(file_spec.GetCString());
53c00cf4a0SZachary Turner         if (!result)
54c00cf4a0SZachary Turner             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
55c00cf4a0SZachary Turner     }
56c00cf4a0SZachary Turner     else
57c00cf4a0SZachary Turner     {
58c00cf4a0SZachary Turner         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
59c00cf4a0SZachary Turner         // indicate the end of the list.
60d3173f34SChaoren Lin         std::string path_buffer{file_spec.GetPath()};
61c00cf4a0SZachary Turner         path_buffer.push_back(0);
62c00cf4a0SZachary Turner 
63c00cf4a0SZachary Turner         SHFILEOPSTRUCT shfos = {0};
64c00cf4a0SZachary Turner         shfos.wFunc = FO_DELETE;
65c00cf4a0SZachary Turner         shfos.pFrom = path_buffer.c_str();
66c00cf4a0SZachary Turner         shfos.fFlags = FOF_NO_UI;
67c00cf4a0SZachary Turner 
68c00cf4a0SZachary Turner         int result = ::SHFileOperation(&shfos);
69c00cf4a0SZachary Turner         // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
70c00cf4a0SZachary Turner         if (result != 0)
71c00cf4a0SZachary Turner             error.SetErrorStringWithFormat("SHFileOperation failed");
72c00cf4a0SZachary Turner     }
73c00cf4a0SZachary Turner     return error;
74c00cf4a0SZachary Turner }
75c00cf4a0SZachary Turner 
76c00cf4a0SZachary Turner Error
77d3173f34SChaoren Lin FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
78c00cf4a0SZachary Turner {
79c00cf4a0SZachary Turner     Error error;
80633594c2SAdrian McCarthy     // Beware that Windows's permission model is different from Unix's, and it's
81633594c2SAdrian McCarthy     // not clear if this API is supposed to check ACLs.  To match the caller's
82633594c2SAdrian McCarthy     // expectations as closely as possible, we'll use Microsoft's _stat, which
83633594c2SAdrian McCarthy     // attempts to emulate POSIX stat.  This should be good enough for basic
84633594c2SAdrian McCarthy     // checks like FileSpec::Readable.
85633594c2SAdrian McCarthy     struct _stat file_stats;
86633594c2SAdrian McCarthy     if (::_stat(file_spec.GetCString(), &file_stats) == 0)
87633594c2SAdrian McCarthy     {
88633594c2SAdrian McCarthy         // The owner permission bits in "st_mode" currently match the definitions
89633594c2SAdrian McCarthy         // for the owner file mode bits.
90633594c2SAdrian McCarthy         file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
91633594c2SAdrian McCarthy     }
92633594c2SAdrian McCarthy     else
93633594c2SAdrian McCarthy     {
94633594c2SAdrian McCarthy         error.SetErrorToErrno();
95633594c2SAdrian McCarthy     }
96633594c2SAdrian McCarthy 
97c00cf4a0SZachary Turner     return error;
98c00cf4a0SZachary Turner }
99c00cf4a0SZachary Turner 
100c00cf4a0SZachary Turner Error
101d3173f34SChaoren Lin FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
102c00cf4a0SZachary Turner {
103c00cf4a0SZachary Turner     Error error;
104c00cf4a0SZachary Turner     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
105c00cf4a0SZachary Turner     return error;
106c00cf4a0SZachary Turner }
107c00cf4a0SZachary Turner 
108c00cf4a0SZachary Turner lldb::user_id_t
109c00cf4a0SZachary Turner FileSystem::GetFileSize(const FileSpec &file_spec)
110c00cf4a0SZachary Turner {
111c00cf4a0SZachary Turner     return file_spec.GetByteSize();
112c00cf4a0SZachary Turner }
113c00cf4a0SZachary Turner 
114c00cf4a0SZachary Turner bool
115c00cf4a0SZachary Turner FileSystem::GetFileExists(const FileSpec &file_spec)
116c00cf4a0SZachary Turner {
117c00cf4a0SZachary Turner     return file_spec.Exists();
118c00cf4a0SZachary Turner }
119c00cf4a0SZachary Turner 
120c00cf4a0SZachary Turner Error
121d3173f34SChaoren Lin FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
122a9ea0711SOleksiy Vyalov {
123a9ea0711SOleksiy Vyalov     Error error;
124d3173f34SChaoren Lin     if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
125a9ea0711SOleksiy Vyalov         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
126a9ea0711SOleksiy Vyalov     return error;
127a9ea0711SOleksiy Vyalov }
128a9ea0711SOleksiy Vyalov 
1292b38f33bSOleksiy Vyalov int
1302b38f33bSOleksiy Vyalov FileSystem::GetHardlinkCount(const FileSpec &file_spec)
1312b38f33bSOleksiy Vyalov {
1322b38f33bSOleksiy Vyalov     HANDLE file_handle = ::CreateFile(file_spec.GetCString(),
1332b38f33bSOleksiy Vyalov                                       FILE_READ_ATTRIBUTES,
1342b38f33bSOleksiy Vyalov                                       FILE_SHARE_READ,
1352b38f33bSOleksiy Vyalov                                       nullptr,
1362b38f33bSOleksiy Vyalov                                       OPEN_EXISTING,
1372b38f33bSOleksiy Vyalov                                       FILE_ATTRIBUTE_NORMAL,
1382b38f33bSOleksiy Vyalov                                       nullptr);
1392b38f33bSOleksiy Vyalov 
1402b38f33bSOleksiy Vyalov     if (file_handle == INVALID_HANDLE_VALUE)
1412b38f33bSOleksiy Vyalov       return -1;
1422b38f33bSOleksiy Vyalov 
1432b38f33bSOleksiy Vyalov     AutoHandle auto_file_handle(file_handle);
1442b38f33bSOleksiy Vyalov     BY_HANDLE_FILE_INFORMATION file_info;
1452b38f33bSOleksiy Vyalov     if (::GetFileInformationByHandle(file_handle, &file_info))
1462b38f33bSOleksiy Vyalov         return file_info.nNumberOfLinks;
1472b38f33bSOleksiy Vyalov 
1482b38f33bSOleksiy Vyalov     return -1;
1492b38f33bSOleksiy Vyalov }
1502b38f33bSOleksiy Vyalov 
151a9ea0711SOleksiy Vyalov Error
152d3173f34SChaoren Lin FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
153c00cf4a0SZachary Turner {
154c00cf4a0SZachary Turner     Error error;
155d3173f34SChaoren Lin     DWORD attrib = ::GetFileAttributes(dst.GetCString());
156c00cf4a0SZachary Turner     if (attrib == INVALID_FILE_ATTRIBUTES)
157c00cf4a0SZachary Turner     {
158c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
159c00cf4a0SZachary Turner         return error;
160c00cf4a0SZachary Turner     }
161c00cf4a0SZachary Turner     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
162c00cf4a0SZachary Turner     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
163d3173f34SChaoren Lin     BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
164c00cf4a0SZachary Turner     if (!result)
165c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
166c00cf4a0SZachary Turner     return error;
167c00cf4a0SZachary Turner }
168c00cf4a0SZachary Turner 
169c00cf4a0SZachary Turner Error
170d3173f34SChaoren Lin FileSystem::Unlink(const FileSpec &file_spec)
171c00cf4a0SZachary Turner {
172c00cf4a0SZachary Turner     Error error;
173d3173f34SChaoren Lin     BOOL result = ::DeleteFile(file_spec.GetCString());
174c00cf4a0SZachary Turner     if (!result)
175c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
176c00cf4a0SZachary Turner     return error;
177c00cf4a0SZachary Turner }
178c00cf4a0SZachary Turner 
179c00cf4a0SZachary Turner Error
180d3173f34SChaoren Lin FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
181c00cf4a0SZachary Turner {
182c00cf4a0SZachary Turner     Error error;
183d3173f34SChaoren Lin     HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
184d3173f34SChaoren Lin             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
185c00cf4a0SZachary Turner             FILE_FLAG_OPEN_REPARSE_POINT, NULL);
186c00cf4a0SZachary Turner     if (h == INVALID_HANDLE_VALUE)
187c00cf4a0SZachary Turner     {
188c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
189c00cf4a0SZachary Turner         return error;
190c00cf4a0SZachary Turner     }
191c00cf4a0SZachary Turner 
192d3173f34SChaoren Lin     char buf[PATH_MAX];
193c00cf4a0SZachary Turner     // Subtract 1 from the path length since this function does not add a null terminator.
194d3173f34SChaoren Lin     DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
195d3173f34SChaoren Lin             FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
196c00cf4a0SZachary Turner     if (result == 0)
197c00cf4a0SZachary Turner         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
198d3173f34SChaoren Lin     else
199d3173f34SChaoren Lin         dst.SetFile(buf, false);
200c00cf4a0SZachary Turner 
201c00cf4a0SZachary Turner     ::CloseHandle(h);
202c00cf4a0SZachary Turner     return error;
203c00cf4a0SZachary Turner }
204736888c8SGreg Clayton 
2059077e9f8SSean Callanan Error
2069077e9f8SSean Callanan FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst)
2079077e9f8SSean Callanan {
2089077e9f8SSean Callanan     return Error("ResolveSymbolicLink() isn't implemented on Windows");
2099077e9f8SSean Callanan }
2109077e9f8SSean Callanan 
211736888c8SGreg Clayton bool
212736888c8SGreg Clayton FileSystem::IsLocal(const FileSpec &spec)
213736888c8SGreg Clayton {
214736888c8SGreg Clayton     if (spec)
215736888c8SGreg Clayton     {
216736888c8SGreg Clayton         // TODO: return true if the file is on a locally mounted file system
217736888c8SGreg Clayton         return true;
218736888c8SGreg Clayton     }
219736888c8SGreg Clayton 
220736888c8SGreg Clayton     return false;
221736888c8SGreg Clayton }
222