Skip to content

Commit

Permalink
Fix bug where temporary file would be left behind every time an archi…
Browse files Browse the repository at this point in the history
…ve was updated.

When updating an existing archive, llvm-ar opens the old archive into a
`MemoryBuffer`, does its thing, and writes the results to a temporary
file. That file is then renamed to the original archive filename, thus
replacing it with the updated contents. However, on Windows at least,
what would happen is that the `MemoryBuffer` for the old archive would
actually be an mmap'ed view of the file, so when it came time to do the
rename via Win32's `ReplaceFile`, it would succeed but would be unable
to fully replace the file since there would still be a handle open on
it; instead, the old version got renamed to a random temporary name and
left behind.

Patch by Cameron!

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268916 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
espindola committed May 9, 2016
1 parent 0f420ad commit 0c06716
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 9 deletions.
2 changes: 1 addition & 1 deletion include/llvm/Object/ArchiveWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class NewArchiveIterator {
std::pair<StringRef, std::error_code>
writeArchive(StringRef ArcName, std::vector<NewArchiveIterator> &NewMembers,
bool WriteSymtab, object::Archive::Kind Kind, bool Deterministic,
bool Thin);
bool Thin, std::unique_ptr<MemoryBuffer> OldArchiveBuf = nullptr);
}

#endif
16 changes: 15 additions & 1 deletion lib/Object/ArchiveWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ std::pair<StringRef, std::error_code>
llvm::writeArchive(StringRef ArcName,
std::vector<NewArchiveIterator> &NewMembers,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin) {
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
assert((!Thin || Kind == object::Archive::K_GNU) &&
"Only the gnu format has a thin mode");
SmallString<128> TmpArchive;
Expand Down Expand Up @@ -443,6 +444,19 @@ llvm::writeArchive(StringRef ArcName,

Output.keep();
Out.close();

// At this point, we no longer need whatever backing memory
// was used to generate the NewMembers. On Windows, this buffer
// could be a mapped view of the file we want to replace (if
// we're updating an existing archive, say). In that case, the
// rename would still succeed, but it would leave behind a
// temporary file (actually the original file renamed) because
// a file cannot be deleted while there's a handle open on it,
// only renamed. So by freeing this buffer, this ensures that
// the last open handle on the destination file, if any, is
// closed before we attempt to rename.
OldArchiveBuf.reset();

sys::fs::rename(TmpArchive, ArcName);
return std::make_pair("", std::error_code());
}
20 changes: 13 additions & 7 deletions tools/llvm-ar/llvm-ar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,9 @@ computeNewArchiveMembers(ArchiveOperation Operation,
}

static void
performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive,
performWriteOperation(ArchiveOperation Operation,
object::Archive *OldArchive,
std::unique_ptr<MemoryBuffer> OldArchiveBuf,
std::vector<NewArchiveIterator> *NewMembersP) {
object::Archive::Kind Kind;
switch (FormatOpt) {
Expand All @@ -599,14 +601,16 @@ performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive,
}
if (NewMembersP) {
std::pair<StringRef, std::error_code> Result = writeArchive(
ArchiveName, *NewMembersP, Symtab, Kind, Deterministic, Thin);
ArchiveName, *NewMembersP, Symtab, Kind, Deterministic, Thin,
std::move(OldArchiveBuf));
failIfError(Result.second, Result.first);
return;
}
std::vector<NewArchiveIterator> NewMembers =
computeNewArchiveMembers(Operation, OldArchive);
auto Result =
writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic, Thin);
writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic, Thin,
std::move(OldArchiveBuf));
failIfError(Result.second, Result.first);
}

Expand All @@ -620,11 +624,12 @@ static void createSymbolTable(object::Archive *OldArchive) {
if (OldArchive->hasSymbolTable())
return;

performWriteOperation(CreateSymTab, OldArchive, nullptr);
performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);
}

static void performOperation(ArchiveOperation Operation,
object::Archive *OldArchive,
std::unique_ptr<MemoryBuffer> OldArchiveBuf,
std::vector<NewArchiveIterator> *NewMembers) {
switch (Operation) {
case Print:
Expand All @@ -637,7 +642,8 @@ static void performOperation(ArchiveOperation Operation,
case Move:
case QuickAppend:
case ReplaceOrInsert:
performWriteOperation(Operation, OldArchive, NewMembers);
performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),
NewMembers);
return;
case CreateSymTab:
createSymbolTable(OldArchive);
Expand All @@ -659,7 +665,7 @@ static int performOperation(ArchiveOperation Operation,
object::Archive Archive(Buf.get()->getMemBufferRef(), EC);
failIfError(EC,
"error loading '" + ArchiveName + "': " + EC.message() + "!");
performOperation(Operation, &Archive, NewMembers);
performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers);
return 0;
}

Expand All @@ -674,7 +680,7 @@ static int performOperation(ArchiveOperation Operation,
}
}

performOperation(Operation, nullptr, NewMembers);
performOperation(Operation, nullptr, nullptr, NewMembers);
return 0;
}

Expand Down

0 comments on commit 0c06716

Please sign in to comment.