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 * 25 FileSystem::DEV_NULL = "nul"; 26 27 const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding"; 28 29 FileSpec::PathSyntax 30 FileSystem::GetNativePathSyntax() 31 { 32 return FileSpec::ePathSyntaxWindows; 33 } 34 35 Error 36 FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) 37 { 38 // On Win32, the mode parameter is ignored, as Windows files and directories support a 39 // different permission model than POSIX. 40 Error error; 41 const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true); 42 if (err_code) 43 { 44 error.SetErrorString(err_code.message().c_str()); 45 } 46 47 return error; 48 } 49 50 Error 51 FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) 52 { 53 Error error; 54 std::wstring path_buffer; 55 if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) 56 { 57 error.SetErrorString(PATH_CONVERSION_ERROR); 58 return error; 59 } 60 if (!recurse) 61 { 62 BOOL result = ::RemoveDirectoryW(path_buffer.c_str()); 63 if (!result) 64 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 65 } 66 else 67 { 68 // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to 69 // indicate the end of the list. The first null terminator is there only in the backing 70 // store but not the actual vector contents, and so we need to push twice. 71 path_buffer.push_back(0); 72 path_buffer.push_back(0); 73 74 SHFILEOPSTRUCTW shfos = {0}; 75 shfos.wFunc = FO_DELETE; 76 shfos.pFrom = (LPCWSTR)path_buffer.data(); 77 shfos.fFlags = FOF_NO_UI; 78 79 int result = ::SHFileOperationW(&shfos); 80 // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values. 81 if (result != 0) 82 error.SetErrorStringWithFormat("SHFileOperation failed"); 83 } 84 return error; 85 } 86 87 Error 88 FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) 89 { 90 Error error; 91 // Beware that Windows's permission model is different from Unix's, and it's 92 // not clear if this API is supposed to check ACLs. To match the caller's 93 // expectations as closely as possible, we'll use Microsoft's _stat, which 94 // attempts to emulate POSIX stat. This should be good enough for basic 95 // checks like FileSpec::Readable. 96 struct _stat file_stats; 97 if (::_stat(file_spec.GetCString(), &file_stats) == 0) 98 { 99 // The owner permission bits in "st_mode" currently match the definitions 100 // for the owner file mode bits. 101 file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); 102 } 103 else 104 { 105 error.SetErrorToErrno(); 106 } 107 108 return error; 109 } 110 111 Error 112 FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) 113 { 114 Error error; 115 error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__); 116 return error; 117 } 118 119 lldb::user_id_t 120 FileSystem::GetFileSize(const FileSpec &file_spec) 121 { 122 return file_spec.GetByteSize(); 123 } 124 125 bool 126 FileSystem::GetFileExists(const FileSpec &file_spec) 127 { 128 return file_spec.Exists(); 129 } 130 131 Error 132 FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) 133 { 134 Error error; 135 std::wstring wsrc, wdst; 136 if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 137 error.SetErrorString(PATH_CONVERSION_ERROR); 138 else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr)) 139 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 140 return error; 141 } 142 143 int 144 FileSystem::GetHardlinkCount(const FileSpec &file_spec) 145 { 146 std::wstring path; 147 if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) 148 return -1; 149 150 HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 151 FILE_ATTRIBUTE_NORMAL, nullptr); 152 153 if (file_handle == INVALID_HANDLE_VALUE) 154 return -1; 155 156 AutoHandle auto_file_handle(file_handle); 157 BY_HANDLE_FILE_INFORMATION file_info; 158 if (::GetFileInformationByHandle(file_handle, &file_info)) 159 return file_info.nNumberOfLinks; 160 161 return -1; 162 } 163 164 Error 165 FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) 166 { 167 Error error; 168 std::wstring wsrc, wdst; 169 if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 170 error.SetErrorString(PATH_CONVERSION_ERROR); 171 if (error.Fail()) 172 return error; 173 DWORD attrib = ::GetFileAttributesW(wdst.c_str()); 174 if (attrib == INVALID_FILE_ATTRIBUTES) 175 { 176 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 177 return error; 178 } 179 bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); 180 DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; 181 BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag); 182 if (!result) 183 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 184 return error; 185 } 186 187 Error 188 FileSystem::Unlink(const FileSpec &file_spec) 189 { 190 Error error; 191 std::wstring path; 192 if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) 193 { 194 error.SetErrorString(PATH_CONVERSION_ERROR); 195 return error; 196 } 197 BOOL result = ::DeleteFileW(path.c_str()); 198 if (!result) 199 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 200 return error; 201 } 202 203 Error 204 FileSystem::Readlink(const FileSpec &src, FileSpec &dst) 205 { 206 Error error; 207 std::wstring wsrc; 208 if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) 209 { 210 error.SetErrorString(PATH_CONVERSION_ERROR); 211 return error; 212 } 213 214 HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 215 FILE_FLAG_OPEN_REPARSE_POINT, NULL); 216 if (h == INVALID_HANDLE_VALUE) 217 { 218 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 219 return error; 220 } 221 222 std::vector<wchar_t> buf(PATH_MAX + 1); 223 // Subtract 1 from the path length since this function does not add a null terminator. 224 DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 225 std::string path; 226 if (result == 0) 227 error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 228 else if (!llvm::convertWideToUTF8(buf.data(), path)) 229 error.SetErrorString(PATH_CONVERSION_ERROR); 230 else 231 dst.SetFile(path, false); 232 233 ::CloseHandle(h); 234 return error; 235 } 236 237 Error 238 FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) 239 { 240 return Error("ResolveSymbolicLink() isn't implemented on Windows"); 241 } 242 243 bool 244 FileSystem::IsLocal(const FileSpec &spec) 245 { 246 if (spec) 247 { 248 // TODO: return true if the file is on a locally mounted file system 249 return true; 250 } 251 252 return false; 253 } 254 255 FILE * 256 FileSystem::Fopen(const char *path, const char *mode) 257 { 258 std::wstring wpath, wmode; 259 if (!llvm::ConvertUTF8toWide(path, wpath)) 260 return nullptr; 261 if (!llvm::ConvertUTF8toWide(mode, wmode)) 262 return nullptr; 263 FILE *file; 264 if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0) 265 return nullptr; 266 return file; 267 } 268 269 int 270 FileSystem::Stat(const char *path, struct stat *stats) 271 { 272 std::wstring wpath; 273 if (!llvm::ConvertUTF8toWide(path, wpath)) 274 { 275 errno = EINVAL; 276 return -EINVAL; 277 } 278 int stat_result; 279 #ifdef _USE_32BIT_TIME_T 280 struct _stat32 file_stats; 281 stat_result = ::_wstat32(wpath.c_str(), &file_stats); 282 #else 283 struct _stat64i32 file_stats; 284 stat_result = ::_wstat64i32(wpath.c_str(), &file_stats); 285 #endif 286 if (stat_result == 0) 287 { 288 static_assert(sizeof(struct stat) == sizeof(file_stats), 289 "stat and _stat32/_stat64i32 must have the same layout"); 290 *stats = *reinterpret_cast<struct stat *>(&file_stats); 291 } 292 return stat_result; 293 } 294