diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -1159,6 +1159,17 @@ /// means that the filesystem may have failed to perform some buffered writes. std::error_code closeFile(file_t &F); +#ifdef LLVM_ON_UNIX +/// @brief Change ownership of a file +/// +/// @param Owner The owner of the file to change to +/// @param Group The group of the file to change to +/// @returns errc::success if successfully updated file ownership, otherwise an +/// error code is returned. +std::error_code changeFileOwnership(const Twine &Name, uint32_t Owner, + uint32_t Group); +#endif + /// RAII class that facilitates file locking. class FileLocker { int FD; ///< Locked file handle. diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -1213,6 +1213,17 @@ return std::error_code(); } +std::error_code changeFileOwnership(const Twine &Name, uint32_t Owner, uint32_t Group) { + int FD; + if (std::error_code EC = sys::fs::openFileForWrite(Name, FD, sys::fs::CD_OpenExisting)) + return EC; + auto Chown = [&]() { return fchown(FD, Owner, Group); }; + if ((sys::RetryAfterSignal(-1, Chown)) < 0) + return std::error_code(errno, std::generic_category()); + if (std::error_code EC = sys::fs::closeFile(FD)) + return EC; +} + } // end namespace fs namespace path { diff --git a/llvm/test/tools/llvm-objcopy/preserve-file-ownership.test b/llvm/test/tools/llvm-objcopy/preserve-file-ownership.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/preserve-file-ownership.test @@ -0,0 +1,31 @@ +# File ownership of the output file is changed to root if it is different from the input. +# RUN: yaml2obj %s -o %t +# RUN: sudo llvm-objcopy %t %t.out +# RUN: ls -l %t.out | FileCheck --strict-whitespace --check-prefix=OBJCOPY-NEW %s +# RUN: yaml2obj %s -o %t +# RUN: sudo llvm-strip %t %t.out +# RUN: ls -l %t.out | FileCheck --strict-whitespace --check-prefix=STRIP-NEW %s + +# File ownership is preserved if the input file is overwritten. +# RUN: yaml2obj %s -o %t +# RUN: sudo llvm-objcopy %t +# RUN: ls -l %t | FileCheck --strict-whitespace --check-prefix=OBJCOPY-OVERWRITE -DUSER=$(stat -c '%U' %s) -DGROUP=$(stat -c '%G' %s) %s +# RUN: yaml2obj %s -o %t +# RUN: sudo llvm-strip %t +# RUN: ls -l %t | FileCheck --strict-whitespace --check-prefix=STRIP-OVERWRITE -DUSER=$(stat -c '%U' %s) -DGROUP=$(stat -c '%G' %s) %s + +# OBJCOPY-NEW: root root +# STRIP-NEW: root root +# OBJCOPY-OVERWRITE: [[USER]] [[GROUP]] +# STRIP-OVERWRITE: [[USER]] [[GROUP]] + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] 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 @@ -328,6 +328,20 @@ return E; } +#if !defined(_WIN32) || defined(__CYGWIN32__) + // Preserve the ownership if the input file is overwritten + if (Config.InputFilename != "-" && + Config.InputFilename == Config.OutputFilename) { + sys::fs::file_status OStat; + if (std::error_code EC = sys::fs::status(Config.OutputFilename, OStat)) + return createFileError(Config.OutputFilename, EC); + if (Stat.getUser() != OStat.getUser() || + Stat.getGroup() != OStat.getGroup()) + sys::fs::changeFileOwnership(Config.OutputFilename, Stat.getUser(), + Stat.getGroup()); + } +#endif + return Error::success(); }