diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -865,6 +865,11 @@ // The open file descriptor. int FD = -1; +#ifdef _WIN32 + // Whether we need to manually remove the file on close. + bool RemoveOnClose = false; +#endif + // Keep this with the given name. Error keep(const Twine &Name); diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -1188,6 +1188,10 @@ FD = Other.FD; Other.Done = true; Other.FD = -1; +#ifdef _WIN32 + RemoveOnClose = Other.RemoveOnClose; + Other.RemoveOnClose = false; +#endif return *this; } @@ -1202,20 +1206,25 @@ FD = -1; #ifdef _WIN32 - // On windows closing will remove the file. - TmpName = ""; - return Error::success(); + // On Windows, closing will remove the file, if we managed to set + // the delete disposition. If not, remove it manually. + bool Remove = RemoveOnClose; #else - // Always try to close and remove. + // Always try to remove the file. + bool Remove = true; +#endif std::error_code RemoveEC; - if (!TmpName.empty()) { + if (Remove && !TmpName.empty()) { RemoveEC = fs::remove(TmpName); +#ifndef _WIN32 sys::DontRemoveFileOnSignal(TmpName); +#endif if (!RemoveEC) TmpName = ""; + } else { + TmpName = ""; } return errorCodeToError(RemoveEC); -#endif } Error TempFile::keep(const Twine &Name) { @@ -1225,20 +1234,26 @@ #ifdef _WIN32 // If we can't cancel the delete don't rename. auto H = reinterpret_cast(_get_osfhandle(FD)); - std::error_code RenameEC = setDeleteDisposition(H, false); + auto TrySetDeleteDisposition = [this](HANDLE H, bool Intent) { + bool Delete = Intent; + std::error_code EC = setDeleteDisposition(H, Delete); + RemoveOnClose = Intent && !Delete; + return EC; + }; + std::error_code RenameEC = TrySetDeleteDisposition(H, false); if (!RenameEC) { RenameEC = rename_handle(H, Name); // If rename failed because it's cross-device, copy instead if (RenameEC == std::error_code(ERROR_NOT_SAME_DEVICE, std::system_category())) { RenameEC = copy_file(TmpName, Name); - setDeleteDisposition(H, true); + TrySetDeleteDisposition(H, true); } } // If we can't rename, discard the temporary file. if (RenameEC) - setDeleteDisposition(H, true); + TrySetDeleteDisposition(H, true); #else std::error_code RenameEC = fs::rename(TmpName, Name); if (RenameEC) { @@ -1269,8 +1284,10 @@ #ifdef _WIN32 auto H = reinterpret_cast(_get_osfhandle(FD)); - if (std::error_code EC = setDeleteDisposition(H, false)) + bool Delete = false; + if (std::error_code EC = setDeleteDisposition(H, Delete)) return errorCodeToError(EC); + RemoveOnClose = false; #else sys::DontRemoveFileOnSignal(TmpName); #endif @@ -1295,7 +1312,22 @@ return errorCodeToError(EC); TempFile Ret(ResultPath, FD); -#ifndef _WIN32 +#ifdef _WIN32 + auto H = reinterpret_cast(_get_osfhandle(FD)); + bool Delete = true; + // Note: This causes duplicate calls to setDeleteDisposition as + // createUniqueFile also calls it internally, but we can't easily know whether + // it succeeded or not. We can't leave out OF_Delete from the call, because + // it's needed to set the DELETE access mode flag. + if (std::error_code EC = setDeleteDisposition(H, Delete)) { + consumeError(Ret.discard()); + return errorCodeToError(EC); + } + // If we weren't able to set the flag for implicitly deleting the file on + // close, make a note to delete it manually. + if (!Delete) + Ret.RemoveOnClose = true; +#else if (sys::RemoveFileOnSignal(ResultPath)) { // Make sure we delete the file when RemoveFileOnSignal fails. consumeError(Ret.discard()); diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -401,7 +401,8 @@ return is_local_internal(FinalPath, Result); } -static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) { +static std::error_code setDeleteDisposition(HANDLE Handle, + bool &WillAutoDelete) { // Clear the FILE_DISPOSITION_INFO flag first, before checking if it's a // network file. On Windows 7 the function realPathFromHandle() below fails // if the FILE_DISPOSITION_INFO flag was already set to 'DeleteFile = true' by @@ -411,7 +412,7 @@ if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition, sizeof(Disposition))) return mapWindowsError(::GetLastError()); - if (!Delete) + if (!WillAutoDelete) return std::error_code(); // Check if the file is on a network (non-local) drive. If so, don't @@ -426,8 +427,10 @@ if (std::error_code EC = is_local_internal(FinalPath, IsLocal)) return EC; - if (!IsLocal) + if (!IsLocal) { + WillAutoDelete = false; return std::error_code(); + } // The file is on a local drive, we can safely set FILE_DISPOSITION_INFO's // flag. @@ -1184,7 +1187,8 @@ } if (Flags & OF_Delete) { - if ((EC = setDeleteDisposition(Result, true))) { + bool ShouldDelete = true; + if ((EC = setDeleteDisposition(Result, ShouldDelete))) { ::CloseHandle(Result); return errorCodeToError(EC); }