diff options
author | Brad King <brad.king@kitware.com> | 2015-09-17 19:34:02 (GMT) |
---|---|---|
committer | CMake Topic Stage <kwrobot@kitware.com> | 2015-09-17 19:34:02 (GMT) |
commit | eb154697c054c43f59dd61bf3edeaf34bf76ad21 (patch) | |
tree | 4b3baa33f9ae150c4a02222a9d9c343b5ec1a139 /Source | |
parent | 0ed4094fadbd5a35677fa0170cd6e469053eb654 (diff) | |
parent | becb14c9552578954bc99e2428b8e6f383eadb55 (diff) | |
download | CMake-eb154697c054c43f59dd61bf3edeaf34bf76ad21.zip CMake-eb154697c054c43f59dd61bf3edeaf34bf76ad21.tar.gz CMake-eb154697c054c43f59dd61bf3edeaf34bf76ad21.tar.bz2 |
Merge topic 'cpack-deb-fakeroot-removal'
becb14c9 CPack/DEB: test preserve extra config file permissions
7044e8ee CPackDeb: use of libarchive and removal of fakeroot
415405a3 cmArchiveWrite: control user/group, permissions and recursive file adding
4f2ff601 Tests: Make RunCMake.CPack error messages more readable
81b748ae cmGeneratedFileStream: Fix spelling in comment
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CPack/cmCPackDebGenerator.cxx | 299 | ||||
-rw-r--r-- | Source/cmArchiveWrite.cxx | 39 | ||||
-rw-r--r-- | Source/cmArchiveWrite.h | 95 | ||||
-rw-r--r-- | Source/cmGeneratedFileStream.h | 4 |
4 files changed, 316 insertions, 121 deletions
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 5049a3f..9402689 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -15,6 +15,7 @@ #include "cmMakefile.h" #include "cmGeneratedFileStream.h" #include "cmCPackLog.h" +#include "cmArchiveWrite.h" #include <cmsys/SystemTools.hxx> #include <cmsys/Glob.hxx> @@ -388,9 +389,9 @@ int cmCPackDebGenerator::createDeb() { std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); dirName += '/'; - for (std::vector<std::string>::const_iterator fileIt = - packageFiles.begin(); - fileIt != packageFiles.end(); ++ fileIt ) + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) { totalSize += cmSystemTools::FileLength(*fileIt); } @@ -401,8 +402,9 @@ int cmCPackDebGenerator::createDeb() out << std::endl; } - std::string cmd(this->GetOption("GEN_CPACK_DEBIAN_FAKEROOT_EXECUTABLE")); + const std::string strGenWDIR(this->GetOption("GEN_WDIR")); + cmArchiveWrite::Compress tar_compression_type = cmArchiveWrite::CompressGZip; const char* debian_compression_type = this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"); if(!debian_compression_type) @@ -410,102 +412,127 @@ int cmCPackDebGenerator::createDeb() debian_compression_type = "gzip"; } - std::string cmake_tar = " ", compression_modifier = "a", compression_suffix; + std::string compression_suffix; if(!strcmp(debian_compression_type, "lzma")) { compression_suffix = ".lzma"; + tar_compression_type = cmArchiveWrite::CompressLZMA; } else if(!strcmp(debian_compression_type, "xz")) { compression_suffix = ".xz"; + tar_compression_type = cmArchiveWrite::CompressXZ; } else if(!strcmp(debian_compression_type, "bzip2")) { compression_suffix = ".bz2"; - compression_modifier = "j"; - cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E "; + tar_compression_type = cmArchiveWrite::CompressBZip2; } else if(!strcmp(debian_compression_type, "gzip")) { compression_suffix = ".gz"; - compression_modifier = "z"; - cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E "; + tar_compression_type = cmArchiveWrite::CompressGZip; } else if(!strcmp(debian_compression_type, "none")) { compression_suffix = ""; - compression_modifier = ""; - cmake_tar += "\"" + cmSystemTools::GetCMakeCommand() + "\" -E "; + tar_compression_type = cmArchiveWrite::CompressNone; } else { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error unrecognized compression type: " << debian_compression_type << std::endl); } - cmd += cmake_tar + "tar c" + compression_modifier + "f data.tar" - + compression_suffix; - // now add all directories which have to be compressed - // collect all top level install dirs for that - // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would give /usr and /opt - size_t topLevelLength = std::string(this->GetOption("GEN_WDIR")).length(); - cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \"" - << this->GetOption("GEN_WDIR") - << "\", length = " << topLevelLength + std::string filename_data_tar = strGenWDIR + + "/data.tar" + compression_suffix; + + // atomic file generation for data.tar + { + cmGeneratedFileStream fileStream_data_tar; + fileStream_data_tar.Open(filename_data_tar.c_str(), false, true); + if(!fileStream_data_tar) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" << filename_data_tar << "\" for writing" << std::endl); - std::set<std::string> installDirs; + return 0; + } + cmArchiveWrite data_tar(fileStream_data_tar, tar_compression_type, "paxr"); + + // uid/gid should be the one of the root user, and this root user has + // always uid/gid equal to 0. + data_tar.SetUIDAndGID(0u, 0u); + data_tar.SetUNAMEAndGNAME("root", "root"); + + // now add all directories which have to be compressed + // collect all top level install dirs for that + // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would + // give /usr and /opt + size_t topLevelLength = strGenWDIR.length(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \"" + << strGenWDIR + << "\", length = " << topLevelLength + << std::endl); + std::set<std::string> orderedFiles; + + // we have to reconstruct the parent folders as well + for (std::vector<std::string>::const_iterator fileIt = - packageFiles.begin(); - fileIt != packageFiles.end(); ++ fileIt ) - { - cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\"" - << std::endl); - std::string::size_type slashPos = fileIt->find('/', topLevelLength+1); - std::string relativeDir = fileIt->substr(topLevelLength, - slashPos - topLevelLength); - cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" << relativeDir - << "\"" << std::endl); - if (installDirs.find(relativeDir) == installDirs.end()) + packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) { - installDirs.insert(relativeDir); - cmd += " ."; - cmd += relativeDir; + std::string currentPath = *fileIt; + while(currentPath != strGenWDIR) + { + // the last one IS strGenWDIR, but we do not want this one: + // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application + // should not add XXX/application + orderedFiles.insert(currentPath); + currentPath = cmSystemTools::CollapseCombinedPath(currentPath, ".."); + } } - } - std::string output; - int retval = -1; - int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, - &retval, this->GetOption("GEN_WDIR"), this->GeneratorVerbose, 0); - if ( !res || retval ) - { - std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - tmpFile += "/Deb.log"; - cmGeneratedFileStream ofs(tmpFile.c_str()); - ofs << "# Run command: " << cmd << std::endl - << "# Working directory: " << toplevel << std::endl - << "# Output:" << std::endl - << output << std::endl; - cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: " - << cmd << std::endl - << "Please check " << tmpFile << " for errors" << std::endl); - return 0; - } + for (std::set<std::string>::const_iterator fileIt = + orderedFiles.begin(); + fileIt != orderedFiles.end(); ++ fileIt ) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\"" + << std::endl); + std::string::size_type slashPos = fileIt->find('/', topLevelLength+1); + std::string relativeDir = fileIt->substr(topLevelLength, + slashPos - topLevelLength); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" << relativeDir + << "\"" << std::endl); - std::string md5filename; - md5filename = this->GetOption("GEN_WDIR"); - md5filename += "/md5sums"; + // do not recurse because the loop will do it + if(!data_tar.Add(*fileIt, topLevelLength, ".", false)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem adding file to tar:" << std::endl + << "#top level directory: " + << strGenWDIR << std::endl + << "#file: " << *fileIt << std::endl + << "#error:" << data_tar.GetError() << std::endl); + return 0; + } + } + } // scope for file generation - { // the scope is needed for cmGeneratedFileStream + + std::string md5filename = strGenWDIR + "/md5sums"; + { + // the scope is needed for cmGeneratedFileStream cmGeneratedFileStream out(md5filename.c_str()); - std::vector<std::string>::const_iterator fileIt; -// std::string topLevelWithTrailingSlash = toplevel; + std::string topLevelWithTrailingSlash = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); topLevelWithTrailingSlash += '/'; - for ( fileIt = packageFiles.begin(); - fileIt != packageFiles.end(); ++ fileIt ) + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) { - cmd = "\""; + std::string cmd = "\""; cmd += cmSystemTools::GetCMakeCommand(); cmd += "\" -E md5sum \""; cmd += *fileIt; cmd += "\""; - //std::string output; - //int retVal = -1; - res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, + + std::string output; + int retval = -1; + int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, &retval, toplevel.c_str(), this->GeneratorVerbose, 0); if ( !res || retval ) { @@ -521,70 +548,116 @@ int cmCPackDebGenerator::createDeb() } // each line contains a eol. // Do not end the md5sum file with yet another (invalid) - } + } - // set md5sum file permissins to RW-R--R-- so that deb lintian doesn't warn - // about it - cmSystemTools::SetPermissions(md5filename.c_str(), - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - cmd = this->GetOption("GEN_CPACK_DEBIAN_FAKEROOT_EXECUTABLE"); - cmd += cmake_tar + "tar czf control.tar.gz ./control ./md5sums"; + + std::string filename_control_tar = strGenWDIR + "/control.tar.gz"; + // atomic file generation for control.tar + { + cmGeneratedFileStream fileStream_control_tar; + fileStream_control_tar.Open(filename_control_tar.c_str(), false, true); + if(!fileStream_control_tar) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" << filename_control_tar + << "\" for writing" << std::endl); + return 0; + } + cmArchiveWrite control_tar(fileStream_control_tar, + tar_compression_type, + "paxr"); + + // sets permissions and uid/gid for the files + control_tar.SetUIDAndGID(0u, 0u); + control_tar.SetUNAMEAndGNAME("root", "root"); + + /* permissions are set according to + https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners + and + https://lintian.debian.org/tags/control-file-has-bad-permissions.html + */ + const mode_t permission644 = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + const mode_t permissionExecute = S_IXUSR | S_IXGRP | S_IXOTH; + const mode_t permission755 = permission644 | permissionExecute; + + // for md5sum and control (that we have generated here), we use 644 + // (RW-R--R--) + // so that deb lintian doesn't warn about it + control_tar.SetPermissions(permission644); + + // adds control and md5sums + if( !control_tar.Add(md5filename, strGenWDIR.length(), ".") + || !control_tar.Add(strGenWDIR + "/control", strGenWDIR.length(), ".")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" << std::endl + << "#top level directory: " + << strGenWDIR << std::endl + << "#file: \"control\" or \"md5sums\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return 0; + } + + // for the other files, we use + // -either the original permission on the files + // -either a permission strictly defined by the Debian policies const char* controlExtra = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"); - if( controlExtra ) - { - std::vector<std::string> controlExtraList; - cmSystemTools::ExpandListArgument(controlExtra, controlExtraList); - for(std::vector<std::string>::iterator i = - controlExtraList.begin(); i != controlExtraList.end(); ++i) + if( controlExtra ) { - std::string filenamename = - cmsys::SystemTools::GetFilenameName(*i); - std::string localcopy = this->GetOption("GEN_WDIR"); - localcopy += "/"; - localcopy += filenamename; - // if we can copy the file, it means it does exist, let's add it: - if( cmsys::SystemTools::CopyFileIfDifferent( - *i, localcopy) ) + // permissions are now controlled by the original file permissions + + const bool permissionStrictPolicy = + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"); + + static const char* strictFiles[] = { + "config", "postinst", "postrm", "preinst", "prerm" + }; + std::set<std::string> setStrictFiles( + strictFiles, + strictFiles + sizeof(strictFiles)/sizeof(strictFiles[0])); + + // default + control_tar.ClearPermissions(); + + std::vector<std::string> controlExtraList; + cmSystemTools::ExpandListArgument(controlExtra, controlExtraList); + for(std::vector<std::string>::iterator i = controlExtraList.begin(); + i != controlExtraList.end(); ++i) { - // debian is picky and need relative to ./ path in the tar.* - cmd += " ./"; - cmd += filenamename; + std::string filenamename = + cmsys::SystemTools::GetFilenameName(*i); + std::string localcopy = strGenWDIR + "/" + filenamename; + + if(permissionStrictPolicy) + { + control_tar.SetPermissions(setStrictFiles.count(filenamename) ? + permission755 : permission644); + } + + // if we can copy the file, it means it does exist, let's add it: + if( cmsys::SystemTools::CopyFileIfDifferent(*i, localcopy) ) + { + control_tar.Add(localcopy, strGenWDIR.length(), "."); + } } } - } - res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &output, - &retval, this->GetOption("GEN_WDIR"), this->GeneratorVerbose, 0); + } - if ( !res || retval ) - { - std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - tmpFile += "/Deb.log"; - cmGeneratedFileStream ofs(tmpFile.c_str()); - ofs << "# Run command: " << cmd << std::endl - << "# Working directory: " << toplevel << std::endl - << "# Output:" << std::endl - << output << std::endl; - cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: " - << cmd << std::endl - << "Please check " << tmpFile << " for errors" << std::endl); - return 0; - } // ar -r your-package-name.deb debian-binary control.tar.* data.tar.* // since debian packages require BSD ar (most Linux distros and even // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here. std::vector<std::string> arFiles; - std::string topLevelString = this->GetOption("GEN_WDIR"); - topLevelString += "/"; + std::string topLevelString = strGenWDIR + "/"; arFiles.push_back(topLevelString + "debian-binary"); arFiles.push_back(topLevelString + "control.tar.gz"); arFiles.push_back(topLevelString + "data.tar" + compression_suffix); - std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - outputFileName += "/"; - outputFileName += this->GetOption("CPACK_OUTPUT_FILE_NAME"); - res = ar_append(outputFileName.c_str(), arFiles); + std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + outputFileName += "/"; + outputFileName += this->GetOption("CPACK_OUTPUT_FILE_NAME"); + int res = ar_append(outputFileName.c_str(), arFiles); if ( res!=0 ) { std::string tmpFile = this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index 9f56324..7946950 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -181,7 +181,10 @@ cmArchiveWrite::~cmArchiveWrite() } //---------------------------------------------------------------------------- -bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix) +bool cmArchiveWrite::Add(std::string path, + size_t skip, + const char* prefix, + bool recursive) { if(this->Okay()) { @@ -189,20 +192,21 @@ bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix) { path.erase(path.size()-1); } - this->AddPath(path.c_str(), skip, prefix); + this->AddPath(path.c_str(), skip, prefix, recursive); } return this->Okay(); } //---------------------------------------------------------------------------- bool cmArchiveWrite::AddPath(const char* path, - size_t skip, const char* prefix) + size_t skip, const char* prefix, + bool recursive) { if(!this->AddFile(path, skip, prefix)) { return false; } - if(!cmSystemTools::FileIsDirectory(path) || + if((!cmSystemTools::FileIsDirectory(path) || !recursive) || cmSystemTools::FileIsSymlink(path)) { return true; @@ -278,6 +282,33 @@ bool cmArchiveWrite::AddFile(const char* file, } archive_entry_set_mtime(e, t, 0); } + + // manages the uid/guid of the entry (if any) + if (this->Uid.IsSet() && this->Gid.IsSet()) + { + archive_entry_set_uid(e, this->Uid.Get()); + archive_entry_set_gid(e, this->Gid.Get()); + } + + if (this->Uname.size() && this->Gname.size()) + { + archive_entry_set_uname(e, this->Uname.c_str()); + archive_entry_set_gname(e, this->Gname.c_str()); + } + + + // manages the permissions + if (this->Permissions.IsSet()) + { + archive_entry_set_perm(e, this->Permissions.Get()); + } + + if (this->PermissionsMask.IsSet()) + { + mode_t perm = archive_entry_perm(e); + archive_entry_set_perm(e, perm & this->PermissionsMask.Get()); + } + // Clear acl and xattr fields not useful for distribution. archive_entry_acl_clear(e); archive_entry_xattr_clear(e); diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h index e6f515d..8dbbb83 100644 --- a/Source/cmArchiveWrite.h +++ b/Source/cmArchiveWrite.h @@ -18,6 +18,22 @@ # error "cmArchiveWrite not allowed during bootstrap build!" #endif +template<typename T> +class cmArchiveWriteOptional +{ +public: + cmArchiveWriteOptional() {this->Clear();} + explicit cmArchiveWriteOptional(T val) {this->Set(val);} + + void Set(T val) {this->IsValueSet = true; this->Value=val;} + void Clear() {this->IsValueSet = false;} + bool IsSet() const {return this->IsValueSet;} + T Get() const {return Value;} +private: + T Value; + bool IsValueSet; +}; + /** \class cmArchiveWrite * \brief Wrapper around libarchive for writing. * @@ -52,7 +68,10 @@ public: * skip. The remaining part of the input path is appended to the * "prefix" value to construct the final name in the archive. */ - bool Add(std::string path, size_t skip = 0, const char* prefix = 0); + bool Add(std::string path, + size_t skip = 0, + const char* prefix = 0, + bool recursive = true); /** Returns true if there has been no error. */ operator safe_bool() const @@ -69,9 +88,65 @@ public: void SetVerbose(bool v) { this->Verbose = v; } void SetMTime(std::string const& t) { this->MTime = t; } + + //! Sets the permissions of the added files/folders + void SetPermissions(mode_t permissions_) + { + this->Permissions.Set(permissions_); + } + + //! Clears permissions - default is used instead + void ClearPermissions() { this->Permissions.Clear(); } + + //! Sets the permissions mask of files/folders + //! + //! The permissions will be copied from the existing file + //! or folder. The mask will then be applied to unset + //! some of them + void SetPermissionsMask(mode_t permissionsMask_) + { + this->PermissionsMask.Set(permissionsMask_); + } + + //! Clears permissions mask - default is used instead + void ClearPermissionsMask() + { + this->PermissionsMask.Clear(); + } + + //! Sets UID and GID to be used in the tar file + void SetUIDAndGID(int uid_, int gid_) + { + this->Uid.Set(uid_); + this->Gid.Set(gid_); + } + + //! Clears UID and GID to be used in the tar file - default is used instead + void ClearUIDAndGID() + { + this->Uid.Clear(); + this->Gid.Clear(); + } + + //! Sets UNAME and GNAME to be used in the tar file + void SetUNAMEAndGNAME(const std::string& uname_, const std::string& gname_) + { + this->Uname = uname_; + this->Gname = gname_; + } + + //! Clears UNAME and GNAME to be used in the tar file + //! default is used instead + void ClearUNAMEAndGNAME() + { + this->Uname = ""; + this->Gname = ""; + } + private: bool Okay() const { return this->Error.empty(); } - bool AddPath(const char* path, size_t skip, const char* prefix); + bool AddPath(const char* path, size_t skip, const char* prefix, + bool recursive = true); bool AddFile(const char* file, size_t skip, const char* prefix); bool AddData(const char* file, size_t size); @@ -87,6 +162,22 @@ private: std::string Format; std::string Error; std::string MTime; + + //! UID of the user in the tar file + cmArchiveWriteOptional<int> Uid; + + //! GUID of the user in the tar file + cmArchiveWriteOptional<int> Gid; + + //! UNAME/GNAME of the user (does not override UID/GID) + //!@{ + std::string Uname; + std::string Gname; + //!@} + + //! Permissions on files/folders + cmArchiveWriteOptional<mode_t> Permissions; + cmArchiveWriteOptional<mode_t> PermissionsMask; }; #endif diff --git a/Source/cmGeneratedFileStream.h b/Source/cmGeneratedFileStream.h index 2aa6beb..8df3e1a 100644 --- a/Source/cmGeneratedFileStream.h +++ b/Source/cmGeneratedFileStream.h @@ -56,10 +56,10 @@ protected: // Whether the real file stream was valid when it was closed. bool Okay; - // Whether the destionation file is compressed + // Whether the destination file is compressed bool Compress; - // Whether the destionation file is compressed + // Whether the destination file is compressed bool CompressExtraExtension; }; |