Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -685,8 +685,7 @@ /// Open the file for read and write. F_RW = 8, - /// The returned handle can be used for deleting the file. Only makes a - /// difference on windows. + /// Delete the file on close. Only makes a difference on windows. F_Delete = 16 }; Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -1068,12 +1068,15 @@ Error TempFile::discard() { Done = true; - // Always try to close and remove. std::error_code RemoveEC; +// On windows closing will remove the file. +#ifndef LLVM_ON_WIN32 + // Always try to close and remove. if (!TmpName.empty()) { RemoveEC = fs::remove(TmpName); sys::DontRemoveFileOnSignal(TmpName); } +#endif if (!RemoveEC) TmpName = ""; @@ -1091,8 +1094,15 @@ assert(!Done); Done = true; // Always try to close and rename. +#ifdef LLVM_ON_WIN32 + // If we cant't cancel the delete don't rename. + std::error_code RenameEC = cancelDeleteOnClose(FD); + if (!RenameEC) + RenameEC = rename_fd(FD, Name); +#else std::error_code RenameEC = fs::rename(TmpName, Name); sys::DontRemoveFileOnSignal(TmpName); +#endif if (!RenameEC) TmpName = ""; @@ -1110,7 +1120,13 @@ assert(!Done); Done = true; +#ifdef LLVM_ON_WIN32 + if (std::error_code EC = cancelDeleteOnClose(FD)) + return errorCodeToError(EC); +#else sys::DontRemoveFileOnSignal(TmpName); +#endif + TmpName = ""; if (close(FD) == -1) { @@ -1125,16 +1141,19 @@ Expected TempFile::create(const Twine &Model, unsigned Mode) { int FD; SmallString<128> ResultPath; - if (std::error_code EC = createUniqueFile(Model, FD, ResultPath, Mode)) + if (std::error_code EC = createUniqueFile(Model, FD, ResultPath, Mode, + sys::fs::F_RW | sys::fs::F_Delete)) return errorCodeToError(EC); - // Make sure we delete the file when RemoveFileOnSignal fails. TempFile Ret(ResultPath, FD); +#ifndef LLVM_ON_WIN32 if (sys::RemoveFileOnSignal(ResultPath)) { + // Make sure we delete the file when RemoveFileOnSignal fails. consumeError(Ret.discard()); std::error_code EC(errc::operation_not_permitted); return errorCodeToError(EC); } +#endif return std::move(Ret); } } Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -391,6 +391,53 @@ return is_local_internal(FinalPath, Result); } +/// 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(_get_osfhandle(FD)); + SmallVector 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()); + + FILE_DISPOSITION_INFO Disposition; + Disposition.DeleteFile = false; + if (!SetFileInformationByHandle(NewHandle, FileDispositionInfo, &Disposition, + sizeof(Disposition))) + return mapWindowsError(::GetLastError()); + 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 ToWide; @@ -513,6 +560,11 @@ return errc::permission_denied; } +static std::error_code rename_fd(int FromFD, const Twine &To) { + HANDLE FromHandle = reinterpret_cast(_get_osfhandle(FromFD)); + return rename_handle(FromHandle, To); +} + std::error_code rename(const Twine &From, const Twine &To) { // Convert to utf-16. SmallVector WideFrom; @@ -1029,15 +1081,18 @@ CreationDisposition = CREATE_ALWAYS; DWORD Access = GENERIC_WRITE; + DWORD Attributes = FILE_ATTRIBUTE_NORMAL; if (Flags & F_RW) Access |= GENERIC_READ; - if (Flags & F_Delete) + if (Flags & F_Delete) { Access |= DELETE; + Attributes |= FILE_FLAG_DELETE_ON_CLOSE; + } HANDLE H = - ::CreateFileW(PathUTF16.begin(), Access, + ::CreateFileW(PathUTF16.data(), Access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + NULL, CreationDisposition, Attributes, NULL); if (H == INVALID_HANDLE_VALUE) { DWORD LastError = ::GetLastError();