[llvm] r319137 - Use FILE_FLAG_DELETE_ON_CLOSE for TempFile on windows.

Rafael Espindola via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 27 17:41:23 PST 2017


Author: rafael
Date: Mon Nov 27 17:41:22 2017
New Revision: 319137

URL: http://llvm.org/viewvc/llvm-project?rev=319137&view=rev
Log:
Use FILE_FLAG_DELETE_ON_CLOSE for TempFile on windows.

We won't see the temp file no more.

Modified:
    llvm/trunk/include/llvm/Support/FileSystem.h
    llvm/trunk/lib/Support/Path.cpp
    llvm/trunk/lib/Support/Windows/Path.inc

Modified: llvm/trunk/include/llvm/Support/FileSystem.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileSystem.h?rev=319137&r1=319136&r2=319137&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileSystem.h (original)
+++ llvm/trunk/include/llvm/Support/FileSystem.h Mon Nov 27 17:41:22 2017
@@ -685,8 +685,7 @@ enum OpenFlags : unsigned {
   /// 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
 };
 

Modified: llvm/trunk/lib/Support/Path.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Path.cpp?rev=319137&r1=319136&r2=319137&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Path.cpp (original)
+++ llvm/trunk/lib/Support/Path.cpp Mon Nov 27 17:41:22 2017
@@ -1068,12 +1068,15 @@ TempFile::~TempFile() { assert(Done); }
 
 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 @@ Error TempFile::keep(const Twine &Name)
   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 @@ Error TempFile::keep() {
   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 @@ Error TempFile::keep() {
 Expected<TempFile> 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);
 }
 }

Modified: llvm/trunk/lib/Support/Windows/Path.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Path.inc?rev=319137&r1=319136&r2=319137&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Path.inc (original)
+++ llvm/trunk/lib/Support/Windows/Path.inc Mon Nov 27 17:41:22 2017
@@ -391,6 +391,53 @@ std::error_code is_local(int FD, bool &R
   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<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());
+
+  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<wchar_t, 0> ToWide;
@@ -513,6 +560,11 @@ static std::error_code rename_handle(HAN
   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;
@@ -1029,15 +1081,18 @@ std::error_code openFileForWrite(const T
     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();




More information about the llvm-commits mailing list