[PATCH] D40811: Delete temp file if rename fails

Rafael Ávila de Espíndola via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 4 15:35:00 PST 2017


rafael created this revision.
Herald added a subscriber: jakehehrlich.

James noticed that when lld failed to replace the output file it would leave the temporary behind. The problem is that the existing logic is

- cancel the delete flag
- rename

We have to cancel first to avoid renaming and then crashing and deleting the old version. What is missing then is deleting the temporary file if the rename fails.

This can be an issue on both unix and windows, but I am not sure how to cause the rename to fail reliably on unix. I think it can be done on ZFS since it has an ACL system similar to what windows uses, but adding support for checking that in llvm-lit is probably not worth it.


https://reviews.llvm.org/D40811

Files:
  lib/Support/Path.cpp
  lib/Support/Windows/Path.inc
  test/tools/llvm-objcopy/cannot-delete-dest.test


Index: test/tools/llvm-objcopy/cannot-delete-dest.test
===================================================================
--- /dev/null
+++ test/tools/llvm-objcopy/cannot-delete-dest.test
@@ -0,0 +1,22 @@
+# REQUIRES: system-windows
+# RUN: icacls %t /grant Everyone:(DC) || true
+# RUN: rm -rf %t
+# RUN: mkdir %t
+# RUN: cd %t
+# RUN: yaml2obj %s > test.o
+# RUN: cp test.o test2.o
+# RUN: icacls test2.o /deny Everyone:(D)
+# RUN: icacls . /deny Everyone:(DC)
+
+# This fails because it cannot replace test2.o
+# RUN: not llvm-objcopy test.o test2.o
+
+# But it doesn't leave any temporary files behind.
+# RUN: not ls test2.o.tmp*
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
Index: lib/Support/Windows/Path.inc
===================================================================
--- lib/Support/Windows/Path.inc
+++ lib/Support/Windows/Path.inc
@@ -391,6 +391,20 @@
   return is_local_internal(FinalPath, Result);
 }
 
+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();
+}
+
+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
@@ -425,11 +439,9 @@
   if (close(FD))
     return mapWindowsError(::GetLastError());
 
-  FILE_DISPOSITION_INFO Disposition;
-  Disposition.DeleteFile = false;
-  if (!SetFileInformationByHandle(NewHandle, FileDispositionInfo, &Disposition,
-                                  sizeof(Disposition)))
-    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);
Index: lib/Support/Path.cpp
===================================================================
--- lib/Support/Path.cpp
+++ lib/Support/Path.cpp
@@ -1099,8 +1099,14 @@
   std::error_code RenameEC = cancelDeleteOnClose(FD);
   if (!RenameEC)
     RenameEC = rename_fd(FD, Name);
+  // If we can't rename, discard the temporary file.
+  if (RenameEC)
+    removeFD(FD);
 #else
   std::error_code RenameEC = fs::rename(TmpName, Name);
+  // If we can't rename, discard the temporary file.
+  if (RenameEC)
+    remove(TmpName);
   sys::DontRemoveFileOnSignal(TmpName);
 #endif
 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D40811.125432.patch
Type: text/x-patch
Size: 2781 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20171204/26ef877c/attachment.bin>


More information about the llvm-commits mailing list