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