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