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