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