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