diff --git a/llvm/include/llvm/Support/FileUtilities.h b/llvm/include/llvm/Support/FileUtilities.h --- a/llvm/include/llvm/Support/FileUtilities.h +++ b/llvm/include/llvm/Support/FileUtilities.h @@ -110,6 +110,29 @@ llvm::Error writeFileAtomically(StringRef TempPathModel, StringRef FinalPath, std::function Writer); + + /// FilePermissionsCarrier helps to copy permissions from the input file to + /// output one. It memorizes the status of the input file and can apply + /// permissions and dates to the output file. + class FilePermissionsCarrier { + public: + static Expected create(StringRef InputFilename); + + /// Apply stored permissions to the \p OutputFilename. + /// Copy LastAccess and ModificationTime if \p CopyDates is true. + /// Overwrite stored permissions if \p OverwritePermissions is specified. + Error apply(StringRef OutputFilename, bool CopyDates = false, + Optional OverwritePermissions = None); + + protected: + FilePermissionsCarrier(StringRef InputFilename, sys::fs::file_status Status) + : InputFilename(InputFilename), InputStatus(Status) {} + Error apply(StringRef OutputFilename, sys::fs::file_status &Status, + bool CopyDates); + + StringRef InputFilename; + sys::fs::file_status InputStatus; + }; } // End llvm namespace #endif diff --git a/llvm/lib/Support/FileUtilities.cpp b/llvm/lib/Support/FileUtilities.cpp --- a/llvm/lib/Support/FileUtilities.cpp +++ b/llvm/lib/Support/FileUtilities.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -323,4 +324,75 @@ return Error::success(); } +Expected +FilePermissionsCarrier::create(StringRef InputFilename) { + sys::fs::file_status Status; + + if (InputFilename != "-") { + if (auto EC = sys::fs::status(InputFilename, Status)) + return createFileError(InputFilename, EC); + } else { + Status.permissions(static_cast(0777)); + } + + return FilePermissionsCarrier(InputFilename, Status); +} + +Error FilePermissionsCarrier::apply( + StringRef OutputFilename, bool CopyDates, + Optional OverwritePermissions) { + sys::fs::file_status Status = InputStatus; + + if (OverwritePermissions) + Status.permissions(*OverwritePermissions); + + return apply(OutputFilename, Status, CopyDates); +} + +Error FilePermissionsCarrier::apply(StringRef OutputFilename, + sys::fs::file_status &Status, + bool CopyDates) { + int FD = 0; + + // Writing to stdout should not be treated as an error here, just + // do not set access/modification times or permissions. + if (OutputFilename == "-") + return Error::success(); + + if (std::error_code EC = sys::fs::openFileForWrite(OutputFilename, FD, + sys::fs::CD_OpenExisting)) + return createFileError(OutputFilename, EC); + + if (CopyDates) + if (std::error_code EC = sys::fs::setLastAccessAndModificationTime( + FD, Status.getLastAccessedTime(), Status.getLastModificationTime())) + return createFileError(OutputFilename, EC); + + sys::fs::file_status OStat; + if (std::error_code EC = sys::fs::status(FD, OStat)) + return createFileError(OutputFilename, EC); + if (OStat.type() == sys::fs::file_type::regular_file) { +#ifndef _WIN32 + // Keep ownership if llvm-objcopy is called under root. + if (OutputFilename == InputFilename && OStat.getUser() == 0) + sys::fs::changeFileOwnership(FD, Status.getUser(), Status.getGroup()); +#endif + + sys::fs::perms Perm = Status.permissions(); + if (OutputFilename != InputFilename) + Perm = static_cast(Perm & ~sys::fs::getUmask() & ~06000); +#ifdef _WIN32 + if (std::error_code EC = sys::fs::setPermissions(OutputFilename, Perm)) +#else + if (std::error_code EC = sys::fs::setPermissions(FD, Perm)) +#endif + return createFileError(OutputFilename, EC); + } + + if (std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD)) + return createFileError(OutputFilename, EC); + + return Error::success(); +} + char llvm::AtomicFileWriteError::ID; 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 @@ -41,6 +41,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Memory.h" @@ -131,66 +132,16 @@ llvm_unreachable("unsupported output format"); } -static Error restoreStatOnFile(StringRef Filename, - const sys::fs::file_status &Stat, - const ConfigManager &ConfigMgr) { - int FD; - const CommonConfig &Config = ConfigMgr.getCommonConfig(); - - // Writing to stdout should not be treated as an error here, just - // do not set access/modification times or permissions. - if (Filename == "-") - return Error::success(); - - if (auto EC = - sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) - return createFileError(Filename, EC); - - if (Config.PreserveDates) - if (auto EC = sys::fs::setLastAccessAndModificationTime( - 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) { -#ifndef _WIN32 - // Keep ownership if llvm-objcopy is called under root. - if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0) - sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup()); -#endif - - sys::fs::perms Perm = Stat.permissions(); - if (Config.InputFilename != Config.OutputFilename) - Perm = static_cast(Perm & ~sys::fs::getUmask() & ~06000); -#ifdef _WIN32 - if (auto EC = sys::fs::setPermissions(Filename, Perm)) -#else - if (auto EC = sys::fs::setPermissions(FD, Perm)) -#endif - return createFileError(Filename, EC); - } - - if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) - return createFileError(Filename, EC); - - return Error::success(); -} - /// The function executeObjcopy does the higher level dispatch based on the type /// of input (raw binary, archive or single object file) and takes care of the /// format-agnostic modifications, i.e. preserving dates. static Error executeObjcopy(ConfigManager &ConfigMgr) { CommonConfig &Config = ConfigMgr.Common; - sys::fs::file_status Stat; - if (Config.InputFilename != "-") { - if (auto EC = sys::fs::status(Config.InputFilename, Stat)) - return createFileError(Config.InputFilename, EC); - } else { - Stat.permissions(static_cast(0777)); - } + Expected PermsCarrier = + FilePermissionsCarrier::create(Config.InputFilename); + if (!PermsCarrier) + return PermsCarrier.takeError(); std::function ObjcopyFunc; @@ -259,14 +210,14 @@ } } - if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr)) + if (Error E = + PermsCarrier->apply(Config.OutputFilename, Config.PreserveDates)) return E; - if (!Config.SplitDWO.empty()) { - Stat.permissions(static_cast(0666)); - if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr)) + if (!Config.SplitDWO.empty()) + if (Error E = PermsCarrier->apply(Config.SplitDWO, Config.PreserveDates, + static_cast(0666))) return E; - } return Error::success(); }