diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
| commit | 044eb2f6afba375a914ac9d8024f8f5142bb912e (patch) | |
| tree | 1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /lib/Support/Windows | |
| parent | eb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff) | |
Notes
Diffstat (limited to 'lib/Support/Windows')
| -rw-r--r-- | lib/Support/Windows/Memory.inc | 80 | ||||
| -rw-r--r-- | lib/Support/Windows/Path.inc | 479 | ||||
| -rw-r--r-- | lib/Support/Windows/Process.inc | 5 | ||||
| -rw-r--r-- | lib/Support/Windows/Program.inc | 57 | ||||
| -rw-r--r-- | lib/Support/Windows/Signals.inc | 29 |
5 files changed, 365 insertions, 285 deletions
diff --git a/lib/Support/Windows/Memory.inc b/lib/Support/Windows/Memory.inc index 7eab9ff3afd2..318e65aaa9ee 100644 --- a/lib/Support/Windows/Memory.inc +++ b/lib/Support/Windows/Memory.inc @@ -160,85 +160,5 @@ void Memory::InvalidateInstructionCache( FlushInstructionCache(GetCurrentProcess(), Addr, Len); } - -MemoryBlock Memory::AllocateRWX(size_t NumBytes, - const MemoryBlock *NearBlock, - std::string *ErrMsg) { - MemoryBlock MB; - std::error_code EC; - MB = allocateMappedMemory(NumBytes, NearBlock, - MF_READ|MF_WRITE|MF_EXEC, EC); - if (EC != std::error_code() && ErrMsg) { - MakeErrMsg(ErrMsg, EC.message()); - } - return MB; -} - -bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) { - std::error_code EC = releaseMappedMemory(M); - if (EC == std::error_code()) - return false; - MakeErrMsg(ErrMsg, EC.message()); - return true; -} - -static DWORD getProtection(const void *addr) { - MEMORY_BASIC_INFORMATION info; - if (sizeof(info) == ::VirtualQuery(addr, &info, sizeof(info))) { - return info.Protect; - } - return 0; -} - -bool Memory::setWritable(MemoryBlock &M, std::string *ErrMsg) { - if (!setRangeWritable(M.Address, M.Size)) { - return MakeErrMsg(ErrMsg, "Cannot set memory to writeable"); - } - return true; -} - -bool Memory::setExecutable(MemoryBlock &M, std::string *ErrMsg) { - if (!setRangeExecutable(M.Address, M.Size)) { - return MakeErrMsg(ErrMsg, "Cannot set memory to executable"); - } - return true; -} - -bool Memory::setRangeWritable(const void *Addr, size_t Size) { - DWORD prot = getProtection(Addr); - if (!prot) - return false; - - if (prot == PAGE_EXECUTE || prot == PAGE_EXECUTE_READ) { - prot = PAGE_EXECUTE_READWRITE; - } else if (prot == PAGE_NOACCESS || prot == PAGE_READONLY) { - prot = PAGE_READWRITE; - } - - DWORD oldProt; - Memory::InvalidateInstructionCache(Addr, Size); - return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt) - == TRUE; -} - -bool Memory::setRangeExecutable(const void *Addr, size_t Size) { - DWORD prot = getProtection(Addr); - if (!prot) - return false; - - if (prot == PAGE_NOACCESS) { - prot = PAGE_EXECUTE; - } else if (prot == PAGE_READONLY) { - prot = PAGE_EXECUTE_READ; - } else if (prot == PAGE_READWRITE) { - prot = PAGE_EXECUTE_READWRITE; - } - - DWORD oldProt; - Memory::InvalidateInstructionCache(Addr, Size); - return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt) - == TRUE; -} - } // namespace sys } // namespace llvm diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index b00d3905f658..f81790b17df5 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -94,13 +94,16 @@ std::error_code widenPath(const Twine &Path8, return EC; FullPath.append(CurPath); } - // Traverse the requested path, canonicalizing . and .. as we go (because - // the \\?\ prefix is documented to treat them as real components). - // The iterators don't report separators and append() always attaches - // preferred_separator so we don't need to call native() on the result. + // Traverse the requested path, canonicalizing . and .. (because the \\?\ + // prefix is documented to treat them as real components). Ignore + // separators, which can be returned from the iterator if the path has a + // drive name. We don't need to call native() on the result since append() + // always attaches preferred_separator. for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str), E = llvm::sys::path::end(Path8Str); I != E; ++I) { + if (I->size() == 1 && is_separator((*I)[0])) + continue; if (I->size() == 1 && *I == ".") continue; if (I->size() == 2 && *I == "..") @@ -165,14 +168,14 @@ ErrorOr<space_info> disk_space(const Twine &Path) { return SpaceInfo; } -TimePoint<> file_status::getLastAccessedTime() const { +TimePoint<> basic_file_status::getLastAccessedTime() const { FILETIME Time; Time.dwLowDateTime = LastAccessedTimeLow; Time.dwHighDateTime = LastAccessedTimeHigh; return toTimePoint(Time); } -TimePoint<> file_status::getLastModificationTime() const { +TimePoint<> basic_file_status::getLastModificationTime() const { FILETIME Time; Time.dwLowDateTime = LastWriteTimeLow; Time.dwHighDateTime = LastWriteTimeHigh; @@ -250,35 +253,38 @@ std::error_code create_link(const Twine &to, const Twine &from) { } std::error_code create_hard_link(const Twine &to, const Twine &from) { - return create_link(to, from); + return create_link(to, from); } std::error_code remove(const Twine &path, bool IgnoreNonExisting) { SmallVector<wchar_t, 128> path_utf16; - file_status ST; - if (std::error_code EC = status(path, ST)) { - if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) - return EC; - return std::error_code(); - } - if (std::error_code ec = widenPath(path, path_utf16)) return ec; - if (ST.type() == file_type::directory_file) { - if (!::RemoveDirectoryW(c_str(path_utf16))) { - std::error_code EC = mapWindowsError(::GetLastError()); - if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) - return EC; - } - return std::error_code(); - } - if (!::DeleteFileW(c_str(path_utf16))) { + // We don't know whether this is a file or a directory, and remove() can + // accept both. The usual way to delete a file or directory is to use one of + // the DeleteFile or RemoveDirectory functions, but that requires you to know + // which one it is. We could stat() the file to determine that, but that would + // cost us additional system calls, which can be slow in a directory + // containing a large number of files. So instead we call CreateFile directly. + // The important part is the FILE_FLAG_DELETE_ON_CLOSE flag, which causes the + // file to be deleted once it is closed. We also use the flags + // FILE_FLAG_BACKUP_SEMANTICS (which allows us to open directories), and + // FILE_FLAG_OPEN_REPARSE_POINT (don't follow symlinks). + ScopedFileHandle h(::CreateFileW( + c_str(path_utf16), DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_DELETE_ON_CLOSE, + NULL)); + if (!h) { std::error_code EC = mapWindowsError(::GetLastError()); if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting) return EC; } + return std::error_code(); } @@ -338,83 +344,261 @@ std::error_code is_local(const Twine &path, bool &result) { return is_local_internal(WidePath, result); } +static std::error_code realPathFromHandle(HANDLE H, + SmallVectorImpl<wchar_t> &Buffer) { + DWORD CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + if (CountChars > Buffer.capacity()) { + // The buffer wasn't big enough, try again. In this case the return value + // *does* indicate the size of the null terminator. + Buffer.reserve(CountChars); + CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + } + if (CountChars == 0) + return mapWindowsError(GetLastError()); + Buffer.set_size(CountChars); + return std::error_code(); +} + +static std::error_code realPathFromHandle(HANDLE H, + SmallVectorImpl<char> &RealPath) { + RealPath.clear(); + SmallVector<wchar_t, MAX_PATH> Buffer; + if (std::error_code EC = realPathFromHandle(H, Buffer)) + return EC; + + const wchar_t *Data = Buffer.data(); + DWORD CountChars = Buffer.size(); + if (CountChars >= 4) { + if (0 == ::memcmp(Data, L"\\\\?\\", 8)) { + CountChars -= 4; + Data += 4; + } + } + + // Convert the result from UTF-16 to UTF-8. + return UTF16ToUTF8(Data, CountChars, RealPath); +} + std::error_code is_local(int FD, bool &Result) { SmallVector<wchar_t, 128> FinalPath; HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); - size_t Len = 128; - do { - FinalPath.reserve(Len); - Len = ::GetFinalPathNameByHandleW(Handle, FinalPath.data(), - FinalPath.capacity() - 1, VOLUME_NAME_NT); - if (Len == 0) - return mapWindowsError(::GetLastError()); - } while (Len > FinalPath.capacity()); - - FinalPath.set_size(Len); + if (std::error_code EC = realPathFromHandle(Handle, FinalPath)) + return EC; return is_local_internal(FinalPath, Result); } -std::error_code rename(const Twine &from, const Twine &to) { - // Convert to utf-16. - SmallVector<wchar_t, 128> wide_from; - SmallVector<wchar_t, 128> wide_to; - if (std::error_code ec = widenPath(from, wide_from)) - return ec; - if (std::error_code ec = widenPath(to, wide_to)) - return ec; +static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) { + FILE_DISPOSITION_INFO Disposition; + Disposition.DeleteFile = Delete; + if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition, + sizeof(Disposition))) + return mapWindowsError(::GetLastError()); + return std::error_code(); +} - std::error_code ec = std::error_code(); +static std::error_code removeFD(int FD) { + HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); + return setDeleteDisposition(Handle, true); +} - // Retry while we see recoverable errors. - // System scanners (eg. indexer) might open the source file when it is written - // and closed. +/// 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()); - bool TryReplace = true; + if (std::error_code EC = setDeleteDisposition(NewHandle, false)) + return EC; - for (int i = 0; i < 2000; i++) { - if (i > 0) - ::Sleep(1); + 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; + if (auto EC = widenPath(To, ToWide)) + return EC; + + std::vector<char> RenameInfoBuf(sizeof(FILE_RENAME_INFO) - sizeof(wchar_t) + + (ToWide.size() * sizeof(wchar_t))); + FILE_RENAME_INFO &RenameInfo = + *reinterpret_cast<FILE_RENAME_INFO *>(RenameInfoBuf.data()); + RenameInfo.ReplaceIfExists = ReplaceIfExists; + RenameInfo.RootDirectory = 0; + RenameInfo.FileNameLength = ToWide.size(); + std::copy(ToWide.begin(), ToWide.end(), &RenameInfo.FileName[0]); + + SetLastError(ERROR_SUCCESS); + if (!SetFileInformationByHandle(FromHandle, FileRenameInfo, &RenameInfo, + RenameInfoBuf.size())) { + unsigned Error = GetLastError(); + if (Error == ERROR_SUCCESS) + Error = ERROR_CALL_NOT_IMPLEMENTED; // Wine doesn't always set error code. + return mapWindowsError(Error); + } - if (TryReplace) { - // Try ReplaceFile first, as it is able to associate a new data stream - // with the destination even if the destination file is currently open. - if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL)) + return std::error_code(); +} + +static std::error_code rename_handle(HANDLE FromHandle, const Twine &To) { + SmallVector<wchar_t, 128> WideTo; + if (std::error_code EC = widenPath(To, WideTo)) + return EC; + + // We normally expect this loop to succeed after a few iterations. If it + // requires more than 200 tries, it's more likely that the failures are due to + // a true error, so stop trying. + for (unsigned Retry = 0; Retry != 200; ++Retry) { + auto EC = rename_internal(FromHandle, To, true); + + if (EC == + std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) { + // Wine doesn't support SetFileInformationByHandle in rename_internal. + // Fall back to MoveFileEx. + SmallVector<wchar_t, MAX_PATH> WideFrom; + if (std::error_code EC2 = realPathFromHandle(FromHandle, WideFrom)) + return EC2; + if (::MoveFileExW(WideFrom.begin(), WideTo.begin(), + MOVEFILE_REPLACE_EXISTING)) return std::error_code(); + return mapWindowsError(GetLastError()); + } - DWORD ReplaceError = ::GetLastError(); - ec = mapWindowsError(ReplaceError); + if (!EC || EC != errc::permission_denied) + return EC; - // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or - // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW(). - if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT || - ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) { - TryReplace = false; + // The destination file probably exists and is currently open in another + // process, either because the file was opened without FILE_SHARE_DELETE or + // it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to + // move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE + // to arrange for the destination file to be deleted when the other process + // closes it. + ScopedFileHandle ToHandle( + ::CreateFileW(WideTo.begin(), GENERIC_READ | DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)); + if (!ToHandle) { + auto EC = mapWindowsError(GetLastError()); + // Another process might have raced with us and moved the existing file + // out of the way before we had a chance to open it. If that happens, try + // to rename the source file again. + if (EC == errc::no_such_file_or_directory) continue; + return EC; + } + + BY_HANDLE_FILE_INFORMATION FI; + if (!GetFileInformationByHandle(ToHandle, &FI)) + return mapWindowsError(GetLastError()); + + // Try to find a unique new name for the destination file. + for (unsigned UniqueId = 0; UniqueId != 200; ++UniqueId) { + std::string TmpFilename = (To + ".tmp" + utostr(UniqueId)).str(); + if (auto EC = rename_internal(ToHandle, TmpFilename, false)) { + if (EC == errc::file_exists || EC == errc::permission_denied) { + // Again, another process might have raced with us and moved the file + // before we could move it. Check whether this is the case, as it + // might have caused the permission denied error. If that was the + // case, we don't need to move it ourselves. + ScopedFileHandle ToHandle2(::CreateFileW( + WideTo.begin(), 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + if (!ToHandle2) { + auto EC = mapWindowsError(GetLastError()); + if (EC == errc::no_such_file_or_directory) + break; + return EC; + } + BY_HANDLE_FILE_INFORMATION FI2; + if (!GetFileInformationByHandle(ToHandle2, &FI2)) + return mapWindowsError(GetLastError()); + if (FI.nFileIndexHigh != FI2.nFileIndexHigh || + FI.nFileIndexLow != FI2.nFileIndexLow || + FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber) + break; + continue; + } + return EC; } - // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry - // using ReplaceFileW(). - if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED) - continue; - // We get ERROR_FILE_NOT_FOUND if the destination file is missing. - // MoveFileEx can handle this case. - if (ReplaceError != ERROR_ACCESS_DENIED && - ReplaceError != ERROR_FILE_NOT_FOUND && - ReplaceError != ERROR_SHARING_VIOLATION) - break; + break; } - if (::MoveFileExW(wide_from.begin(), wide_to.begin(), - MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) - return std::error_code(); + // Okay, the old destination file has probably been moved out of the way at + // this point, so try to rename the source file again. Still, another + // process might have raced with us to create and open the destination + // file, so we need to keep doing this until we succeed. + } + + // The most likely root cause. + return errc::permission_denied; +} + +static std::error_code rename_fd(int FromFD, const Twine &To) { + HANDLE FromHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FromFD)); + return rename_handle(FromHandle, To); +} + +std::error_code rename(const Twine &From, const Twine &To) { + // Convert to utf-16. + SmallVector<wchar_t, 128> WideFrom; + if (std::error_code EC = widenPath(From, WideFrom)) + return EC; - DWORD MoveError = ::GetLastError(); - ec = mapWindowsError(MoveError); - if (MoveError != ERROR_ACCESS_DENIED) break; + ScopedFileHandle FromHandle; + // Retry this a few times to defeat badly behaved file system scanners. + for (unsigned Retry = 0; Retry != 200; ++Retry) { + if (Retry != 0) + ::Sleep(10); + FromHandle = + ::CreateFileW(WideFrom.begin(), GENERIC_READ | DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (FromHandle) + break; } + if (!FromHandle) + return mapWindowsError(GetLastError()); - return ec; + return rename_handle(FromHandle, To); } std::error_code resize_file(int FD, uint64_t Size) { @@ -502,6 +686,15 @@ static bool isReservedName(StringRef path) { return false; } +static file_type file_type_from_attrs(DWORD Attrs) { + return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file + : file_type::regular_file; +} + +static perms perms_from_attrs(DWORD Attrs) { + return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all; +} + static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { if (FileHandle == INVALID_HANDLE_VALUE) goto handle_status_error; @@ -530,22 +723,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { if (!::GetFileInformationByHandle(FileHandle, &Info)) goto handle_status_error; - { - file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - ? file_type::directory_file - : file_type::regular_file; - perms Permissions = (Info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - ? (all_read | all_exe) - : all_all; - Result = file_status( - Type, Permissions, Info.nNumberOfLinks, - Info.ftLastAccessTime.dwHighDateTime, - Info.ftLastAccessTime.dwLowDateTime, - Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, - Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, - Info.nFileIndexHigh, Info.nFileIndexLow); - return std::error_code(); - } + Result = file_status( + file_type_from_attrs(Info.dwFileAttributes), + perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks, + Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime, + Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, + Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, + Info.nFileIndexHigh, Info.nFileIndexLow); + return std::error_code(); handle_status_error: DWORD LastError = ::GetLastError(); @@ -637,10 +822,6 @@ std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { std::error_code mapped_file_region::init(int FD, uint64_t Offset, mapmode Mode) { - // Make sure that the requested size fits within SIZE_T. - if (Size > std::numeric_limits<SIZE_T>::max()) - return make_error_code(errc::invalid_argument); - HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); if (FileHandle == INVALID_HANDLE_VALUE) return make_error_code(errc::bad_file_descriptor); @@ -654,8 +835,8 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, HANDLE FileMappingHandle = ::CreateFileMappingW(FileHandle, 0, flprotect, - (Offset + Size) >> 32, - (Offset + Size) & 0xffffffff, + Hi_32(Size), + Lo_32(Size), 0); if (FileMappingHandle == NULL) { std::error_code ec = mapWindowsError(GetLastError()); @@ -697,7 +878,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, return std::error_code(); } -mapped_file_region::mapped_file_region(int fd, mapmode mode, uint64_t length, +mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset, std::error_code &ec) : Size(length), Mapping() { ec = init(fd, offset, mode); @@ -710,7 +891,7 @@ mapped_file_region::~mapped_file_region() { ::UnmapViewOfFile(Mapping); } -uint64_t mapped_file_region::size() const { +size_t mapped_file_region::size() const { assert(Mapping && "Mapping failed but used anyway!"); return Size; } @@ -731,6 +912,16 @@ int mapped_file_region::alignment() { return SysInfo.dwAllocationGranularity; } +static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) { + return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes), + perms_from_attrs(FindData->dwFileAttributes), + FindData->ftLastAccessTime.dwHighDateTime, + FindData->ftLastAccessTime.dwLowDateTime, + FindData->ftLastWriteTime.dwHighDateTime, + FindData->ftLastWriteTime.dwLowDateTime, + FindData->nFileSizeHigh, FindData->nFileSizeLow); +} + std::error_code detail::directory_iterator_construct(detail::DirIterState &it, StringRef path, bool follow_symlinks) { @@ -751,7 +942,9 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, // Get the first directory entry. WIN32_FIND_DATAW FirstFind; - ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind)); + ScopedFindHandle FindHandle(::FindFirstFileExW( + c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, + NULL, FIND_FIRST_EX_LARGE_FETCH)); if (!FindHandle) return mapWindowsError(::GetLastError()); @@ -778,7 +971,8 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, it.IterationHandle = intptr_t(FindHandle.take()); SmallString<128> directory_entry_path(path); path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks); + it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks, + status_from_find_data(&FirstFind)); return std::error_code(); } @@ -814,36 +1008,13 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { directory_entry_path_utf8)) return ec; - it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8)); + it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8), + status_from_find_data(&FindData)); return std::error_code(); } -static std::error_code realPathFromHandle(HANDLE H, - SmallVectorImpl<char> &RealPath) { - RealPath.clear(); - llvm::SmallVector<wchar_t, MAX_PATH> Buffer; - DWORD CountChars = ::GetFinalPathNameByHandleW( - H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); - if (CountChars > Buffer.capacity()) { - // The buffer wasn't big enough, try again. In this case the return value - // *does* indicate the size of the null terminator. - Buffer.reserve(CountChars); - CountChars = ::GetFinalPathNameByHandleW( - H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); - } - if (CountChars == 0) - return mapWindowsError(GetLastError()); - - const wchar_t *Data = Buffer.data(); - if (CountChars >= 4) { - if (0 == ::memcmp(Data, L"\\\\?\\", 8)) { - CountChars -= 4; - Data += 4; - } - } - - // Convert the result from UTF-16 to UTF-8. - return UTF16ToUTF8(Data, CountChars, RealPath); +ErrorOr<basic_file_status> directory_entry::status() const { + return Status; } static std::error_code directoryRealPath(const Twine &Name, @@ -922,12 +1093,18 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, 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.begin(), Access, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE H = + ::CreateFileW(PathUTF16.data(), Access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, CreationDisposition, Attributes, NULL); if (H == INVALID_HANDLE_VALUE) { DWORD LastError = ::GetLastError(); @@ -959,42 +1136,6 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, return std::error_code(); } -std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) { - HANDLE FileHandle = reinterpret_cast<HANDLE>(::_get_osfhandle(FD)); - if (FileHandle == INVALID_HANDLE_VALUE) - return make_error_code(errc::bad_file_descriptor); - - DWORD CharCount; - SmallVector<wchar_t, 1024> TempPath; - do { - CharCount = ::GetFinalPathNameByHandleW(FileHandle, TempPath.begin(), - TempPath.capacity(), - FILE_NAME_NORMALIZED); - if (CharCount < TempPath.capacity()) - break; - - // Reserve sufficient space for the path as well as the null character. Even - // though the API does not document that it is required, if we reserve just - // CharCount space, the function call will not store the resulting path and - // still report success. - TempPath.reserve(CharCount + 1); - } while (true); - - if (CharCount == 0) - return mapWindowsError(::GetLastError()); - - TempPath.set_size(CharCount); - - // On earlier Windows releases, the character count includes the terminating - // null. - if (TempPath.back() == L'\0') { - --CharCount; - TempPath.pop_back(); - } - - return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath); -} - std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { // Convert to utf-16. SmallVector<wchar_t, 128> Path16; diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index 18aef610d54a..3fe9f89f1ef5 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -129,9 +129,10 @@ Optional<std::string> Process::GetEnv(StringRef Name) { size_t Size = MAX_PATH; do { Buf.reserve(Size); + SetLastError(NO_ERROR); Size = - GetEnvironmentVariableW(NameUTF16.data(), Buf.data(), Buf.capacity()); - if (Size == 0) + GetEnvironmentVariableW(NameUTF16.data(), Buf.data(), Buf.capacity()); + if (Size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) return None; // Try again with larger buffer. diff --git a/lib/Support/Windows/Program.inc b/lib/Support/Windows/Program.inc index 721167da5b15..52921cd6a203 100644 --- a/lib/Support/Windows/Program.inc +++ b/lib/Support/Windows/Program.inc @@ -103,9 +103,10 @@ ErrorOr<std::string> sys::findProgramByName(StringRef Name, return std::string(U8Result.begin(), U8Result.end()); } -static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { +static HANDLE RedirectIO(Optional<StringRef> Path, int fd, + std::string *ErrMsg) { HANDLE h; - if (path == 0) { + if (!Path) { if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd), GetCurrentProcess(), &h, 0, TRUE, DUPLICATE_SAME_ACCESS)) @@ -114,10 +115,10 @@ static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { } std::string fname; - if (path->empty()) + if (Path->empty()) fname = "NUL"; else - fname = *path; + fname = *Path; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); @@ -125,7 +126,7 @@ static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { sa.bInheritHandle = TRUE; SmallVector<wchar_t, 128> fnameUnicode; - if (path->empty()) { + if (Path->empty()) { // Don't play long-path tricks on "NUL". if (windows::UTF8ToUTF16(fname, fnameUnicode)) return INVALID_HANDLE_VALUE; @@ -207,19 +208,19 @@ static unsigned int ArgLenWithQuotes(const char *Str) { } -static std::unique_ptr<char[]> flattenArgs(const char **args) { +static std::unique_ptr<char[]> flattenArgs(const char **Args) { // First, determine the length of the command line. unsigned len = 0; - for (unsigned i = 0; args[i]; i++) { - len += ArgLenWithQuotes(args[i]) + 1; + for (unsigned i = 0; Args[i]; i++) { + len += ArgLenWithQuotes(Args[i]) + 1; } // Now build the command line. std::unique_ptr<char[]> command(new char[len+1]); char *p = command.get(); - for (unsigned i = 0; args[i]; i++) { - const char *arg = args[i]; + for (unsigned i = 0; Args[i]; i++) { + const char *arg = Args[i]; const char *start = arg; bool needsQuoting = ArgNeedsQuotes(arg); @@ -248,9 +249,9 @@ static std::unique_ptr<char[]> flattenArgs(const char **args) { return command; } -static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, - const char **envp, const StringRef **redirects, - unsigned memoryLimit, std::string *ErrMsg) { +static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, + const char **Envp, ArrayRef<Optional<StringRef>> Redirects, + unsigned MemoryLimit, std::string *ErrMsg) { if (!sys::fs::can_execute(Program)) { if (ErrMsg) *ErrMsg = "program not executable"; @@ -268,18 +269,18 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, // Windows wants a command line, not an array of args, to pass to the new // process. We have to concatenate them all, while quoting the args that // have embedded spaces (or are empty). - std::unique_ptr<char[]> command = flattenArgs(args); + std::unique_ptr<char[]> command = flattenArgs(Args); // The pointer to the environment block for the new process. std::vector<wchar_t> EnvBlock; - if (envp) { + if (Envp) { // An environment block consists of a null-terminated block of // null-terminated strings. Convert the array of environment variables to // an environment block by concatenating them. - for (unsigned i = 0; envp[i]; ++i) { + for (unsigned i = 0; Envp[i]; ++i) { SmallVector<wchar_t, MAX_PATH> EnvString; - if (std::error_code ec = windows::UTF8ToUTF16(envp[i], EnvString)) { + if (std::error_code ec = windows::UTF8ToUTF16(Envp[i], EnvString)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); return false; @@ -299,21 +300,21 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdError = INVALID_HANDLE_VALUE; - if (redirects) { + if (!Redirects.empty()) { si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg); + si.hStdInput = RedirectIO(Redirects[0], 0, ErrMsg); if (si.hStdInput == INVALID_HANDLE_VALUE) { MakeErrMsg(ErrMsg, "can't redirect stdin"); return false; } - si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg); + si.hStdOutput = RedirectIO(Redirects[1], 1, ErrMsg); if (si.hStdOutput == INVALID_HANDLE_VALUE) { CloseHandle(si.hStdInput); MakeErrMsg(ErrMsg, "can't redirect stdout"); return false; } - if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) { + if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) { // If stdout and stderr should go to the same place, redirect stderr // to the handle already open for stdout. if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, @@ -326,7 +327,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, } } else { // Just redirect stderr - si.hStdError = RedirectIO(redirects[2], 2, ErrMsg); + si.hStdError = RedirectIO(Redirects[2], 2, ErrMsg); if (si.hStdError == INVALID_HANDLE_VALUE) { CloseHandle(si.hStdInput); CloseHandle(si.hStdOutput); @@ -386,14 +387,14 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, // Assign the process to a job if a memory limit is defined. ScopedJobHandle hJob; - if (memoryLimit != 0) { + if (MemoryLimit != 0) { hJob = CreateJobObjectW(0, 0); bool success = false; if (hJob) { JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; memset(&jeli, 0, sizeof(jeli)); jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; - jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576; + jeli.ProcessMemoryLimit = uintptr_t(MemoryLimit) * 1048576; if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { if (AssignProcessToJobObject(hJob, pi.hProcess)) @@ -534,16 +535,16 @@ llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, return EC; } -bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, ArrayRef<const char*> Args) { +bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, + ArrayRef<const char *> Args) { // The documented max length of the command line passed to CreateProcess. static const size_t MaxCommandStringLength = 32768; // Account for the trailing space for the program path and the // trailing NULL of the last argument. size_t ArgLength = ArgLenWithQuotes(Program.str().c_str()) + 2; - for (ArrayRef<const char*>::iterator I = Args.begin(), E = Args.end(); - I != E; ++I) { + for (const char* Arg : Args) { // Account for the trailing space for every arg - ArgLength += ArgLenWithQuotes(*I) + 1; + ArgLength += ArgLenWithQuotes(Arg) + 1; if (ArgLength > MaxCommandStringLength) { return false; } diff --git a/lib/Support/Windows/Signals.inc b/lib/Support/Windows/Signals.inc index 1ef51888baf3..21dd2dd13754 100644 --- a/lib/Support/Windows/Signals.inc +++ b/lib/Support/Windows/Signals.inc @@ -212,8 +212,14 @@ static StringRef Argv0; enum { #if defined(_M_X64) NativeMachineType = IMAGE_FILE_MACHINE_AMD64 -#else +#elif defined(_M_ARM64) + NativeMachineType = IMAGE_FILE_MACHINE_ARM64 +#elif defined(_M_IX86) NativeMachineType = IMAGE_FILE_MACHINE_I386 +#elif defined(_M_ARM) + NativeMachineType = IMAGE_FILE_MACHINE_ARMNT +#else + NativeMachineType = IMAGE_FILE_MACHINE_UNKNOWN #endif }; @@ -318,18 +324,18 @@ static void PrintStackTraceForThread(llvm::raw_ostream &OS, HANDLE hProcess, using namespace llvm; // Print the PC in hexadecimal. DWORD64 PC = StackFrame.AddrPC.Offset; -#if defined(_M_X64) +#if defined(_M_X64) || defined(_M_ARM64) OS << format("0x%016llX", PC); -#elif defined(_M_IX86) +#elif defined(_M_IX86) || defined(_M_ARM) OS << format("0x%08lX", static_cast<DWORD>(PC)); #endif // Print the parameters. Assume there are four. -#if defined(_M_X64) +#if defined(_M_X64) || defined(_M_ARM64) OS << format(" (0x%016llX 0x%016llX 0x%016llX 0x%016llX)", StackFrame.Params[0], StackFrame.Params[1], StackFrame.Params[2], StackFrame.Params[3]); -#elif defined(_M_IX86) +#elif defined(_M_IX86) || defined(_M_ARM) OS << format(" (0x%08lX 0x%08lX 0x%08lX 0x%08lX)", static_cast<DWORD>(StackFrame.Params[0]), static_cast<DWORD>(StackFrame.Params[1]), @@ -526,10 +532,14 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS) { StackFrame.AddrPC.Offset = Context.Rip; StackFrame.AddrStack.Offset = Context.Rsp; StackFrame.AddrFrame.Offset = Context.Rbp; -#else +#elif defined(_M_IX86) StackFrame.AddrPC.Offset = Context.Eip; StackFrame.AddrStack.Offset = Context.Esp; StackFrame.AddrFrame.Offset = Context.Ebp; +#elif defined(_M_ARM64) || defined(_M_ARM) + StackFrame.AddrPC.Offset = Context.Pc; + StackFrame.AddrStack.Offset = Context.Sp; + StackFrame.AddrFrame.Offset = Context.Fp; #endif StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrStack.Mode = AddrModeFlat; @@ -804,6 +814,13 @@ static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp; StackFrame.AddrFrame.Mode = AddrModeFlat; +#elif defined(_M_ARM64) || defined(_M_ARM) + StackFrame.AddrPC.Offset = ep->ContextRecord->Pc; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = ep->ContextRecord->Sp; + StackFrame.AddrStack.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = ep->ContextRecord->Fp; + StackFrame.AddrFrame.Mode = AddrModeFlat; #endif HANDLE hProcess = GetCurrentProcess(); |
