diff options
Diffstat (limited to 'lib/Support/Windows/Path.inc')
-rw-r--r-- | lib/Support/Windows/Path.inc | 379 |
1 files changed, 217 insertions, 162 deletions
diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index f81790b17df5..f425d607af47 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/WindowsError.h" #include <fcntl.h> #include <io.h> @@ -45,6 +46,7 @@ typedef int errno_t; using namespace llvm; using llvm::sys::windows::UTF8ToUTF16; +using llvm::sys::windows::CurCPToUTF16; using llvm::sys::windows::UTF16ToUTF8; using llvm::sys::path::widenPath; @@ -121,6 +123,8 @@ std::error_code widenPath(const Twine &Path8, namespace fs { +const file_t kInvalidFile = INVALID_HANDLE_VALUE; + std::string getMainExecutable(const char *argv0, void *MainExecAddr) { SmallVector<wchar_t, MAX_PATH> PathName; DWORD Size = ::GetModuleFileNameW(NULL, PathName.data(), PathName.capacity()); @@ -400,56 +404,6 @@ static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) { return std::error_code(); } -static std::error_code removeFD(int FD) { - HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); - return setDeleteDisposition(Handle, true); -} - -/// In order to handle temporary files we want the following properties -/// -/// * The temporary file is deleted on crashes -/// * We can use (read, rename, etc) the temporary file. -/// * We can cancel the delete to keep the file. -/// -/// Using FILE_DISPOSITION_INFO with DeleteFile=true will create a file that is -/// deleted on close, but it has a few problems: -/// -/// * The file cannot be used. An attempt to open or rename the file will fail. -/// This makes the temporary file almost useless, as it cannot be part of -/// any other CreateFileW call in the current or in another process. -/// * It is not atomic. A crash just after CreateFileW or just after canceling -/// the delete will leave the file on disk. -/// -/// Using FILE_FLAG_DELETE_ON_CLOSE solves the first issues and the first part -/// of the second one, but there is no way to cancel it in place. What works is -/// to create a second handle to prevent the deletion, close the first one and -/// then clear DeleteFile with SetFileInformationByHandle. This requires -/// changing the handle and file descriptor the caller uses. -static std::error_code cancelDeleteOnClose(int &FD) { - HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); - SmallVector<wchar_t, MAX_PATH> Name; - if (std::error_code EC = realPathFromHandle(Handle, Name)) - return EC; - HANDLE NewHandle = - ::CreateFileW(Name.data(), GENERIC_READ | GENERIC_WRITE | DELETE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (NewHandle == INVALID_HANDLE_VALUE) - return mapWindowsError(::GetLastError()); - if (close(FD)) - return mapWindowsError(::GetLastError()); - - if (std::error_code EC = setDeleteDisposition(NewHandle, false)) - return EC; - - FD = ::_open_osfhandle(intptr_t(NewHandle), 0); - if (FD == -1) { - ::CloseHandle(NewHandle); - return mapWindowsError(ERROR_INVALID_HANDLE); - } - return std::error_code(); -} - static std::error_code rename_internal(HANDLE FromHandle, const Twine &To, bool ReplaceIfExists) { SmallVector<wchar_t, 0> ToWide; @@ -822,8 +776,9 @@ std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { std::error_code mapped_file_region::init(int FD, uint64_t Offset, mapmode Mode) { - HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); - if (FileHandle == INVALID_HANDLE_VALUE) + this->Mode = Mode; + HANDLE OrigFileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); + if (OrigFileHandle == INVALID_HANDLE_VALUE) return make_error_code(errc::bad_file_descriptor); DWORD flprotect; @@ -834,7 +789,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, } HANDLE FileMappingHandle = - ::CreateFileMappingW(FileHandle, 0, flprotect, + ::CreateFileMappingW(OrigFileHandle, 0, flprotect, Hi_32(Size), Lo_32(Size), 0); @@ -872,9 +827,20 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, Size = mbi.RegionSize; } - // Close all the handles except for the view. It will keep the other handles - // alive. + // Close the file mapping handle, as it's kept alive by the file mapping. But + // neither the file mapping nor the file mapping handle keep the file handle + // alive, so we need to keep a reference to the file in case all other handles + // are closed and the file is deleted, which may cause invalid data to be read + // from the file. ::CloseHandle(FileMappingHandle); + if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle, + ::GetCurrentProcess(), &FileHandle, 0, 0, + DUPLICATE_SAME_ACCESS)) { + std::error_code ec = mapWindowsError(GetLastError()); + ::UnmapViewOfFile(Mapping); + return ec; + } + return std::error_code(); } @@ -887,8 +853,20 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, } mapped_file_region::~mapped_file_region() { - if (Mapping) + if (Mapping) { ::UnmapViewOfFile(Mapping); + + if (Mode == mapmode::readwrite) { + // There is a Windows kernel bug, the exact trigger conditions of which + // are not well understood. When triggered, dirty pages are not properly + // flushed and subsequent process's attempts to read a file can return + // invalid data. Calling FlushFileBuffers on the write handle is + // sufficient to ensure that this bug is not triggered. + ::FlushFileBuffers(FileHandle); + } + + ::CloseHandle(FileHandle); + } } size_t mapped_file_region::size() const { @@ -1017,35 +995,82 @@ ErrorOr<basic_file_status> directory_entry::status() const { return Status; } -static std::error_code directoryRealPath(const Twine &Name, - SmallVectorImpl<char> &RealPath) { - SmallVector<wchar_t, 128> PathUTF16; +static std::error_code nativeFileToFd(Expected<HANDLE> H, int &ResultFD, + OpenFlags Flags) { + int CrtOpenFlags = 0; + if (Flags & OF_Append) + CrtOpenFlags |= _O_APPEND; - if (std::error_code EC = widenPath(Name, PathUTF16)) - return EC; + if (Flags & OF_Text) + CrtOpenFlags |= _O_TEXT; - HANDLE H = - ::CreateFileW(PathUTF16.begin(), GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (H == INVALID_HANDLE_VALUE) - return mapWindowsError(GetLastError()); - std::error_code EC = realPathFromHandle(H, RealPath); - ::CloseHandle(H); - return EC; + ResultFD = -1; + if (!H) + return errorToErrorCode(H.takeError()); + + ResultFD = ::_open_osfhandle(intptr_t(*H), CrtOpenFlags); + if (ResultFD == -1) { + ::CloseHandle(*H); + return mapWindowsError(ERROR_INVALID_HANDLE); + } + return std::error_code(); } -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - SmallVectorImpl<char> *RealPath) { +static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) { + // This is a compatibility hack. Really we should respect the creation + // disposition, but a lot of old code relied on the implicit assumption that + // OF_Append implied it would open an existing file. Since the disposition is + // now explicit and defaults to CD_CreateAlways, this assumption would cause + // any usage of OF_Append to append to a new file, even if the file already + // existed. A better solution might have two new creation dispositions: + // CD_AppendAlways and CD_AppendNew. This would also address the problem of + // OF_Append being used on a read-only descriptor, which doesn't make sense. + if (Flags & OF_Append) + return OPEN_ALWAYS; + + switch (Disp) { + case CD_CreateAlways: + return CREATE_ALWAYS; + case CD_CreateNew: + return CREATE_NEW; + case CD_OpenAlways: + return OPEN_ALWAYS; + case CD_OpenExisting: + return OPEN_EXISTING; + } + llvm_unreachable("unreachable!"); +} + +static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) { + DWORD Result = 0; + if (Access & FA_Read) + Result |= GENERIC_READ; + if (Access & FA_Write) + Result |= GENERIC_WRITE; + if (Flags & OF_Delete) + Result |= DELETE; + if (Flags & OF_UpdateAtime) + Result |= FILE_WRITE_ATTRIBUTES; + return Result; +} + +static std::error_code openNativeFileInternal(const Twine &Name, + file_t &ResultFile, DWORD Disp, + DWORD Access, DWORD Flags, + bool Inherit = false) { SmallVector<wchar_t, 128> PathUTF16; - if (std::error_code EC = widenPath(Name, PathUTF16)) return EC; + SECURITY_ATTRIBUTES SA; + SA.nLength = sizeof(SA); + SA.lpSecurityDescriptor = nullptr; + SA.bInheritHandle = Inherit; + HANDLE H = - ::CreateFileW(PathUTF16.begin(), GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ::CreateFileW(PathUTF16.begin(), Access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA, + Disp, Flags, NULL); if (H == INVALID_HANDLE_VALUE) { DWORD LastError = ::GetLastError(); std::error_code EC = mapWindowsError(LastError); @@ -1058,82 +1083,96 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, return make_error_code(errc::is_a_directory); return EC; } + ResultFile = H; + return std::error_code(); +} - int FD = ::_open_osfhandle(intptr_t(H), 0); - if (FD == -1) { - ::CloseHandle(H); - return mapWindowsError(ERROR_INVALID_HANDLE); +Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, + unsigned Mode) { + // Verify that we don't have both "append" and "excl". + assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) && + "Cannot specify both 'CreateNew' and 'Append' file creation flags!"); + + DWORD NativeDisp = nativeDisposition(Disp, Flags); + DWORD NativeAccess = nativeAccess(Access, Flags); + + bool Inherit = false; + if (Flags & OF_ChildInherit) + Inherit = true; + + file_t Result; + std::error_code EC = openNativeFileInternal( + Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit); + if (EC) + return errorCodeToError(EC); + + if (Flags & OF_UpdateAtime) { + FILETIME FileTime; + SYSTEMTIME SystemTime; + GetSystemTime(&SystemTime); + if (SystemTimeToFileTime(&SystemTime, &FileTime) == 0 || + SetFileTime(Result, NULL, &FileTime, NULL) == 0) { + DWORD LastError = ::GetLastError(); + ::CloseHandle(Result); + return errorCodeToError(mapWindowsError(LastError)); + } } - // Fetch the real name of the file, if the user asked - if (RealPath) - realPathFromHandle(H, *RealPath); - - ResultFD = FD; - return std::error_code(); + if (Flags & OF_Delete) { + if ((EC = setDeleteDisposition(Result, true))) { + ::CloseHandle(Result); + return errorCodeToError(EC); + } + } + return Result; } -std::error_code openFileForWrite(const Twine &Name, int &ResultFD, - sys::fs::OpenFlags Flags, unsigned Mode) { - // Verify that we don't have both "append" and "excl". - assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) && - "Cannot specify both 'excl' and 'append' file creation flags!"); +std::error_code openFile(const Twine &Name, int &ResultFD, + CreationDisposition Disp, FileAccess Access, + OpenFlags Flags, unsigned int Mode) { + Expected<file_t> Result = openNativeFile(Name, Disp, Access, Flags); + if (!Result) + return errorToErrorCode(Result.takeError()); - SmallVector<wchar_t, 128> PathUTF16; + return nativeFileToFd(*Result, ResultFD, Flags); +} - if (std::error_code EC = widenPath(Name, PathUTF16)) +static std::error_code directoryRealPath(const Twine &Name, + SmallVectorImpl<char> &RealPath) { + file_t File; + std::error_code EC = openNativeFileInternal( + Name, File, OPEN_EXISTING, GENERIC_READ, FILE_FLAG_BACKUP_SEMANTICS); + if (EC) return EC; - DWORD CreationDisposition; - if (Flags & F_Excl) - CreationDisposition = CREATE_NEW; - else if (Flags & F_Append) - CreationDisposition = OPEN_ALWAYS; - else - CreationDisposition = CREATE_ALWAYS; - - DWORD Access = GENERIC_WRITE; - DWORD Attributes = FILE_ATTRIBUTE_NORMAL; - if (Flags & F_RW) - Access |= GENERIC_READ; - if (Flags & F_Delete) { - Access |= DELETE; - Attributes |= FILE_FLAG_DELETE_ON_CLOSE; - } - - HANDLE H = - ::CreateFileW(PathUTF16.data(), Access, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, CreationDisposition, Attributes, NULL); + EC = realPathFromHandle(File, RealPath); + ::CloseHandle(File); + return EC; +} - if (H == INVALID_HANDLE_VALUE) { - DWORD LastError = ::GetLastError(); - std::error_code EC = mapWindowsError(LastError); - // Provide a better error message when trying to open directories. - // This only runs if we failed to open the file, so there is probably - // no performances issues. - if (LastError != ERROR_ACCESS_DENIED) - return EC; - if (is_directory(Name)) - return make_error_code(errc::is_a_directory); - return EC; - } +std::error_code openFileForRead(const Twine &Name, int &ResultFD, + OpenFlags Flags, + SmallVectorImpl<char> *RealPath) { + Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath); + return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None); +} - int OpenFlags = 0; - if (Flags & F_Append) - OpenFlags |= _O_APPEND; +Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags, + SmallVectorImpl<char> *RealPath) { + Expected<file_t> Result = + openNativeFile(Name, CD_OpenExisting, FA_Read, Flags); - if (Flags & F_Text) - OpenFlags |= _O_TEXT; + // Fetch the real name of the file, if the user asked + if (Result && RealPath) + realPathFromHandle(*Result, *RealPath); - int FD = ::_open_osfhandle(intptr_t(H), OpenFlags); - if (FD == -1) { - ::CloseHandle(H); - return mapWindowsError(ERROR_INVALID_HANDLE); - } + return Result; +} - ResultFD = FD; - return std::error_code(); +void closeFile(file_t &F) { + ::CloseHandle(F); + F = kInvalidFile; } std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { @@ -1204,7 +1243,8 @@ std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, return directoryRealPath(path, dest); int fd; - if (std::error_code EC = llvm::sys::fs::openFileForRead(path, fd, &dest)) + if (std::error_code EC = + llvm::sys::fs::openFileForRead(path, fd, OF_None, &dest)) return EC; ::close(fd); return std::error_code(); @@ -1279,23 +1319,26 @@ void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) { } // end namespace path namespace windows { -std::error_code UTF8ToUTF16(llvm::StringRef utf8, - llvm::SmallVectorImpl<wchar_t> &utf16) { - if (!utf8.empty()) { - int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), - utf8.size(), utf16.begin(), 0); - - if (len == 0) +std::error_code CodePageToUTF16(unsigned codepage, + llvm::StringRef original, + llvm::SmallVectorImpl<wchar_t> &utf16) { + if (!original.empty()) { + int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), 0); + + if (len == 0) { return mapWindowsError(::GetLastError()); + } utf16.reserve(len + 1); utf16.set_size(len); - len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), - utf8.size(), utf16.begin(), utf16.size()); + len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), utf16.size()); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } } // Make utf16 null terminated. @@ -1305,32 +1348,44 @@ std::error_code UTF8ToUTF16(llvm::StringRef utf8, return std::error_code(); } +std::error_code UTF8ToUTF16(llvm::StringRef utf8, + llvm::SmallVectorImpl<wchar_t> &utf16) { + return CodePageToUTF16(CP_UTF8, utf8, utf16); +} + +std::error_code CurCPToUTF16(llvm::StringRef curcp, + llvm::SmallVectorImpl<wchar_t> &utf16) { + return CodePageToUTF16(CP_ACP, curcp, utf16); +} + static std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16, size_t utf16_len, - llvm::SmallVectorImpl<char> &utf8) { + llvm::SmallVectorImpl<char> &converted) { if (utf16_len) { // Get length. - int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.begin(), + int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(), 0, NULL, NULL); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } - utf8.reserve(len); - utf8.set_size(len); + converted.reserve(len); + converted.set_size(len); // Now do the actual conversion. - len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.data(), - utf8.size(), NULL, NULL); + len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(), + converted.size(), NULL, NULL); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } } - // Make utf8 null terminated. - utf8.push_back(0); - utf8.pop_back(); + // Make the new string null terminated. + converted.push_back(0); + converted.pop_back(); return std::error_code(); } @@ -1341,8 +1396,8 @@ std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, } std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, - llvm::SmallVectorImpl<char> &utf8) { - return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8); + llvm::SmallVectorImpl<char> &curcp) { + return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp); } } // end namespace windows |