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