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