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 lldb::user_id_t FileSystem::GetFileSize(const FileSpec &file_spec) {
34   return file_spec.GetByteSize();
35 }
36 
37 bool FileSystem::GetFileExists(const FileSpec &file_spec) {
38   return file_spec.Exists();
39 }
40 
41 Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) {
42   Error error;
43   std::wstring wsrc, wdst;
44   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) ||
45       !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
46     error.SetErrorString(PATH_CONVERSION_ERROR);
47   else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr))
48     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
49   return error;
50 }
51 
52 int FileSystem::GetHardlinkCount(const FileSpec &file_spec) {
53   std::wstring path;
54   if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
55     return -1;
56 
57   HANDLE file_handle =
58       ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
59                     nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
60 
61   if (file_handle == INVALID_HANDLE_VALUE)
62     return -1;
63 
64   AutoHandle auto_file_handle(file_handle);
65   BY_HANDLE_FILE_INFORMATION file_info;
66   if (::GetFileInformationByHandle(file_handle, &file_info))
67     return file_info.nNumberOfLinks;
68 
69   return -1;
70 }
71 
72 Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) {
73   Error error;
74   std::wstring wsrc, wdst;
75   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) ||
76       !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
77     error.SetErrorString(PATH_CONVERSION_ERROR);
78   if (error.Fail())
79     return error;
80   DWORD attrib = ::GetFileAttributesW(wdst.c_str());
81   if (attrib == INVALID_FILE_ATTRIBUTES) {
82     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
83     return error;
84   }
85   bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
86   DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
87   BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag);
88   if (!result)
89     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
90   return error;
91 }
92 
93 Error FileSystem::Unlink(const FileSpec &file_spec) {
94   Error error;
95   std::wstring path;
96   if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) {
97     error.SetErrorString(PATH_CONVERSION_ERROR);
98     return error;
99   }
100   BOOL result = ::DeleteFileW(path.c_str());
101   if (!result)
102     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
103   return error;
104 }
105 
106 Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) {
107   Error error;
108   std::wstring wsrc;
109   if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) {
110     error.SetErrorString(PATH_CONVERSION_ERROR);
111     return error;
112   }
113 
114   HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ,
115                            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
116                            OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);
117   if (h == INVALID_HANDLE_VALUE) {
118     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
119     return error;
120   }
121 
122   std::vector<wchar_t> buf(PATH_MAX + 1);
123   // Subtract 1 from the path length since this function does not add a null
124   // terminator.
125   DWORD result = ::GetFinalPathNameByHandleW(
126       h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
127   std::string path;
128   if (result == 0)
129     error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
130   else if (!llvm::convertWideToUTF8(buf.data(), path))
131     error.SetErrorString(PATH_CONVERSION_ERROR);
132   else
133     dst.SetFile(path, false);
134 
135   ::CloseHandle(h);
136   return error;
137 }
138 
139 Error FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) {
140   return Error("ResolveSymbolicLink() isn't implemented on Windows");
141 }
142 
143 bool FileSystem::IsLocal(const FileSpec &spec) {
144   if (spec) {
145     // TODO: return true if the file is on a locally mounted file system
146     return true;
147   }
148 
149   return false;
150 }
151 
152 FILE *FileSystem::Fopen(const char *path, const char *mode) {
153   std::wstring wpath, wmode;
154   if (!llvm::ConvertUTF8toWide(path, wpath))
155     return nullptr;
156   if (!llvm::ConvertUTF8toWide(mode, wmode))
157     return nullptr;
158   FILE *file;
159   if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0)
160     return nullptr;
161   return file;
162 }
163 
164 int FileSystem::Stat(const char *path, struct stat *stats) {
165   std::wstring wpath;
166   if (!llvm::ConvertUTF8toWide(path, wpath)) {
167     errno = EINVAL;
168     return -EINVAL;
169   }
170   int stat_result;
171 #ifdef _USE_32BIT_TIME_T
172   struct _stat32 file_stats;
173   stat_result = ::_wstat32(wpath.c_str(), &file_stats);
174 #else
175   struct _stat64i32 file_stats;
176   stat_result = ::_wstat64i32(wpath.c_str(), &file_stats);
177 #endif
178   if (stat_result == 0) {
179     static_assert(sizeof(struct stat) == sizeof(file_stats),
180                   "stat and _stat32/_stat64i32 must have the same layout");
181     *stats = *reinterpret_cast<struct stat *>(&file_stats);
182   }
183   return stat_result;
184 }
185