diff --git a/llvm/test/tools/llvm-objcopy/ELF/mirror-permissions-unix.test b/llvm/test/tools/llvm-objcopy/ELF/mirror-permissions-unix.test --- a/llvm/test/tools/llvm-objcopy/ELF/mirror-permissions-unix.test +++ b/llvm/test/tools/llvm-objcopy/ELF/mirror-permissions-unix.test @@ -26,16 +26,22 @@ # RUN: llvm-objcopy %t %t1 # RUN: ls -l %t1 | cut -f 1 -d ' ' > %t1.perms # RUN: cmp %t1.perms %t.0777 +# RUN: llvm-objcopy %t +# RUN: ls -l %t1 | cut -f 1 -d ' ' | cmp %t.0777 - # RUN: chmod 0666 %t # RUN: llvm-objcopy %t %t1 # RUN: ls -l %t1 | cut -f 1 -d ' ' > %t1.perms # RUN: cmp %t1.perms %t.0666 +# RUN: llvm-objcopy %t +# RUN: ls -l %t1 | cut -f 1 -d ' ' | cmp %t.0666 - # RUN: chmod 0640 %t # RUN: llvm-objcopy %t %t1 # RUN: ls -l %t1 | cut -f 1 -d ' ' > %t1.perms # RUN: cmp %t1.perms %t.0640 +# RUN: llvm-objcopy %t +# RUN: ls -l %t1 | cut -f 1 -d ' ' | cmp %t.0640 - ## Don't set the permission of a character special file, otherwise there will ## be an EPERM error (or worse: root may change the permission). diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -228,9 +228,21 @@ Config.DeterministicArchives, Ar.isThin()); } +static Error createOrOverwrite(StringRef Filename, ArrayRef Buf) { + int FD; + std::error_code EC; + if (auto EC = sys::fs::openFileForWrite( + Filename, FD, sys::fs::CD_CreateAlways, sys::fs::OF_None, + sys::fs::all_read | sys::fs::all_write)) + return errorCodeToError(EC); + raw_fd_ostream(FD, /*shouldClose=*/true, /*unbuffered=*/true) + << StringRef(Buf.data(), Buf.size()); + return Error::success(); +} + static Error restoreStatOnFile(StringRef Filename, const sys::fs::file_status &Stat, - bool PreserveDates) { + bool PreserveDates, bool PreserveModes) { int FD; // Writing to stdout should not be treated as an error here, just @@ -247,20 +259,22 @@ FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) return createFileError(Filename, EC); - sys::fs::file_status OStat; - if (std::error_code EC = sys::fs::status(FD, OStat)) - return createFileError(Filename, EC); - if (OStat.type() == sys::fs::file_type::regular_file) + if (PreserveModes) { + sys::fs::file_status OStat; + if (std::error_code EC = sys::fs::status(FD, OStat)) + return createFileError(Filename, EC); + if (OStat.type() == sys::fs::file_type::regular_file) #ifdef _WIN32 - if (auto EC = sys::fs::setPermissions( - Filename, static_cast(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions( + Filename, static_cast(Stat.permissions() & + ~sys::fs::getUmask()))) #else - if (auto EC = sys::fs::setPermissions( - FD, static_cast(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions( + FD, static_cast(Stat.permissions() & + ~(sys::fs::getUmask() | 06000)))) #endif - return createFileError(Filename, EC); + return createFileError(Filename, EC); + } if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) return createFileError(Filename, EC); @@ -293,6 +307,7 @@ ProcessRaw = nullptr; } + bool PreserveModes = true; if (ProcessRaw) { auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); if (!BufOrErr) @@ -309,6 +324,23 @@ if (Archive *Ar = dyn_cast(BinaryOrErr.get().getBinary())) { if (Error E = executeObjcopyOnArchive(Config, *Ar)) return E; + } else if (Config.InputFilename == Config.OutputFilename && + Config.InputFilename != "-" && !Config.BuildIdLinkInput && + !Config.BuildIdLinkOutput) { + // If input filename = output filename, create an in-memory buffer and + // overwrite the input instead of atomic rename to avoid owner/group/mode + // changes. --build-id-link-{input,output} need the on-disk file, so + // overwrite cannot be used. + MemBuffer MB(Config.OutputFilename); + if (Error E = executeObjcopyOnBinary(Config, + *BinaryOrErr.get().getBinary(), MB)) + return E; + std::unique_ptr WMB = MB.releaseMemoryBuffer(); + // Release the existing handle before overwriting. + (void)BinaryOrErr->takeBinary(); + if (Error E = createOrOverwrite(Config.InputFilename, WMB->getBuffer())) + return E; + PreserveModes = false; } else { FileBuffer FB(Config.OutputFilename); if (Error E = executeObjcopyOnBinary(Config, @@ -317,14 +349,14 @@ } } - if (Error E = - restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, + Config.PreserveDates, PreserveModes)) return E; if (!Config.SplitDWO.empty()) { Stat.permissions(static_cast(0666)); - if (Error E = - restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates, + true)) return E; }