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