From 3f439e2b7126fb82952cd7bc12b8d6cb01352219 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 18 Aug 2013 23:17:05 +0200 Subject: add a simple container for HashStrings APT supports more than just one HashString and even allows to enforce the usage of a specific hash. This class is intended to help with storage and passing around of the HashStrings. The cherry-pick here the un-const-ification of HashType() compared to f4c3850ea335545e297504941dc8c7a8f1c83358. The point of this commit is adding infrastructure for the next one. All by itself, it just adds new symbols. Git-Dch: Ignore --- apt-pkg/contrib/hashes.cc | 118 ++++++++++++++++++++++++++++++++++++++++------ apt-pkg/contrib/hashes.h | 84 ++++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 15 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc index 15f83615d..bb11a3fca 100644 --- a/apt-pkg/contrib/hashes.cc +++ b/apt-pkg/contrib/hashes.cc @@ -27,7 +27,7 @@ #include /*}}}*/ -const char* HashString::_SupportedHashes[] = +const char * HashString::_SupportedHashes[] = { "SHA512", "SHA256", "SHA1", "MD5Sum", NULL }; @@ -42,11 +42,16 @@ HashString::HashString(std::string Type, std::string Hash) : Type(Type), Hash(Ha HashString::HashString(std::string StringedHash) /*{{{*/ { - // legacy: md5sum without "MD5Sum:" prefix - if (StringedHash.find(":") == std::string::npos && StringedHash.size() == 32) + if (StringedHash.find(":") == std::string::npos) { - Type = "MD5Sum"; - Hash = StringedHash; + // legacy: md5sum without "MD5Sum:" prefix + if (StringedHash.size() == 32) + { + Type = "MD5Sum"; + Hash = StringedHash; + } + if(_config->FindB("Debug::Hashes",false) == true) + std::clog << "HashString(string): invalid StringedHash " << StringedHash << std::endl; return; } std::string::size_type pos = StringedHash.find(":"); @@ -82,25 +87,25 @@ std::string HashString::GetHashForFile(std::string filename) const /*{{{*/ std::string fileHash; FileFd Fd(filename, FileFd::ReadOnly); - if(Type == "MD5Sum") + if(strcasecmp(Type.c_str(), "MD5Sum") == 0) { MD5Summation MD5; MD5.AddFD(Fd); fileHash = (std::string)MD5.Result(); } - else if (Type == "SHA1") + else if (strcasecmp(Type.c_str(), "SHA1") == 0) { SHA1Summation SHA1; SHA1.AddFD(Fd); fileHash = (std::string)SHA1.Result(); } - else if (Type == "SHA256") + else if (strcasecmp(Type.c_str(), "SHA256") == 0) { SHA256Summation SHA256; SHA256.AddFD(Fd); fileHash = (std::string)SHA256.Result(); } - else if (Type == "SHA512") + else if (strcasecmp(Type.c_str(), "SHA512") == 0) { SHA512Summation SHA512; SHA512.AddFD(Fd); @@ -111,20 +116,105 @@ std::string HashString::GetHashForFile(std::string filename) const /*{{{*/ return fileHash; } /*}}}*/ -const char** HashString::SupportedHashes() +const char** HashString::SupportedHashes() /*{{{*/ { return _SupportedHashes; } - -APT_PURE bool HashString::empty() const + /*}}}*/ +APT_PURE bool HashString::empty() const /*{{{*/ { return (Type.empty() || Hash.empty()); } + /*}}}*/ +std::string HashString::toStr() const /*{{{*/ +{ + return Type + ":" + Hash; +} + /*}}}*/ +APT_PURE bool HashString::operator==(HashString const &other) const /*{{{*/ +{ + return (strcasecmp(Type.c_str(), other.Type.c_str()) == 0 && Hash == other.Hash); +} +APT_PURE bool HashString::operator!=(HashString const &other) const +{ + return !(*this == other); +} + /*}}}*/ + +HashString const * HashStringList::find(char const * const type) const /*{{{*/ +{ + if (type == NULL || type[0] == '\0') + { + std::string forcedType = _config->Find("Acquire::ForceHash", ""); + if (forcedType.empty() == false) + return find(forcedType.c_str()); + for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t) + for (std::vector::const_iterator hs = list.begin(); hs != list.end(); ++hs) + if (strcasecmp(hs->HashType().c_str(), *t) == 0) + return &*hs; + return NULL; + } + for (std::vector::const_iterator hs = list.begin(); hs != list.end(); ++hs) + if (strcasecmp(hs->HashType().c_str(), type) == 0) + return &*hs; + return NULL; +} + /*}}}*/ +bool HashStringList::supported(char const * const type) /*{{{*/ +{ + for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t) + if (strcasecmp(*t, type) == 0) + return true; + return false; +} + /*}}}*/ +bool HashStringList::push_back(const HashString &hashString) /*{{{*/ +{ + if (hashString.HashType().empty() == true || + hashString.HashValue().empty() == true || + supported(hashString.HashType().c_str()) == false) + return false; + + // ensure that each type is added only once + HashString const * const hs = find(hashString.HashType().c_str()); + if (hs != NULL) + return *hs == hashString; -std::string HashString::toStr() const + list.push_back(hashString); + return true; +} + /*}}}*/ +bool HashStringList::VerifyFile(std::string filename) const /*{{{*/ { - return Type + std::string(":") + Hash; + if (list.empty() == true) + return false; + HashString const * const hs = find(NULL); + if (hs == NULL || hs->VerifyFile(filename) == false) + return false; + return true; } + /*}}}*/ +bool HashStringList::operator==(HashStringList const &other) const /*{{{*/ +{ + short matches = 0; + for (const_iterator hs = begin(); hs != end(); ++hs) + { + HashString const * const ohs = other.find(hs->HashType()); + if (ohs == NULL) + continue; + if (*hs != *ohs) + return false; + ++matches; + } + if (matches == 0) + return false; + return true; +} +bool HashStringList::operator!=(HashStringList const &other) const +{ + return !(*this == other); +} + /*}}}*/ // Hashes::AddFD - Add the contents of the FD /*{{{*/ // --------------------------------------------------------------------- diff --git a/apt-pkg/contrib/hashes.h b/apt-pkg/contrib/hashes.h index 7a62f8a8f..5a4213868 100644 --- a/apt-pkg/contrib/hashes.h +++ b/apt-pkg/contrib/hashes.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ class HashString protected: std::string Type; std::string Hash; - static const char* _SupportedHashes[10]; + static const char * _SupportedHashes[10]; // internal helper std::string GetHashForFile(std::string filename) const; @@ -53,6 +54,8 @@ class HashString // get hash type used std::string HashType() { return Type; }; + std::string HashType() const { return Type; }; + std::string HashValue() const { return Hash; }; // verify the given filename against the currently loaded hash bool VerifyFile(std::string filename) const; @@ -64,11 +67,90 @@ class HashString // helper std::string toStr() const; // convert to str as "type:hash" bool empty() const; + bool operator==(HashString const &other) const; + bool operator!=(HashString const &other) const; // return the list of hashes we support static APT_CONST const char** SupportedHashes(); }; +class HashStringList +{ + public: + /** find best hash if no specific one is requested + * + * @param type of the checksum to return, can be \b NULL + * @return If type is \b NULL (or the empty string) it will + * return the 'best' hash; otherwise the hash which was + * specifically requested. If no hash is found \b NULL will be returned. + */ + HashString const * find(char const * const type) const; + HashString const * find(std::string const &type) const { return find(type.c_str()); } + /** check if the given hash type is supported + * + * @param type to check + * @return true if supported, otherwise false + */ + static APT_PURE bool supported(char const * const type); + /** add the given #HashString to the list + * + * @param hashString to add + * @return true if the hash is added because it is supported and + * not already a different hash of the same type included, otherwise false + */ + bool push_back(const HashString &hashString); + /** @return size of the list of HashStrings */ + size_t size() const { return list.size(); } + + /** take the 'best' hash and verify file with it + * + * @param filename to verify + * @return true if the file matches the hashsum, otherwise false + */ + bool VerifyFile(std::string filename) const; + + /** is the list empty ? + * + * @return \b true if the list is empty, otherwise \b false + */ + bool empty() const { return list.empty(); } + + typedef std::vector::const_iterator const_iterator; + + /** iterator to the first element */ + const_iterator begin() const { return list.begin(); } + + /** iterator to the end element */ + const_iterator end() const { return list.end(); } + + /** start fresh with a clear list */ + void clear() { list.clear(); } + + /** compare two HashStringList for similarity. + * + * Two lists are similar if at least one hashtype is in both lists + * and the hashsum matches. All hashes are checked, if one doesn't + * match false is returned regardless of how many matched before. + */ + bool operator==(HashStringList const &other) const; + bool operator!=(HashStringList const &other) const; + + HashStringList() {} + + // simplifying API-compatibility constructors + HashStringList(std::string const &hash) { + if (hash.empty() == false) + list.push_back(HashString(hash)); + } + HashStringList(char const * const hash) { + if (hash != NULL && hash[0] != '\0') + list.push_back(HashString(hash)); + } + + private: + std::vector list; +}; + class Hashes { public: -- cgit v1.2.3-70-g09d2 From 3a2b39ee602dd5a98b8fdaee2f1c8e0b13a276e2 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 18 Aug 2013 23:27:24 +0200 Subject: use 'best' hash for source authentication Collect all hashes we can get from the source record and put them into a HashStringList so that 'apt-get source' can use it instead of using always the MD5sum. We therefore also deprecate the MD5 struct member in favor of the list. While at it, the parsing of the Files is enhanced so that records which miss "Files" (aka MD5 checksums) are still searched for other checksums as they include just as much data, just not with a nice and catchy name. This is a cherry-pick of 1262d35 with some dirty tricks to preserve ABI. LP: 1098738 --- apt-pkg/deb/debsrcrecords.cc | 162 +++++++++---- apt-pkg/deb/debsrcrecords.h | 1 + apt-pkg/srcrecords.cc | 31 ++- apt-pkg/srcrecords.h | 21 +- cmdline/apt-get.cc | 28 ++- debian/libapt-pkg4.12.symbols | 2 + .../test-ubuntu-bug-1098738-apt-get-source-md5sum | 260 +++++++++++++++++++++ 7 files changed, 445 insertions(+), 60 deletions(-) create mode 100755 test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum (limited to 'apt-pkg') diff --git a/apt-pkg/deb/debsrcrecords.cc b/apt-pkg/deb/debsrcrecords.cc index a444cbe4d..49a348dd4 100644 --- a/apt-pkg/deb/debsrcrecords.cc +++ b/apt-pkg/deb/debsrcrecords.cc @@ -118,13 +118,32 @@ bool debSrcRecordParser::BuildDepends(std::vector &List) +bool debSrcRecordParser::Files(std::vector &F) { - List.erase(List.begin(),List.end()); - - string Files = Sect.FindS("Files"); - if (Files.empty() == true) + std::vector F2; + if (Files2(F2) == false) return false; + for (std::vector::const_iterator f2 = F2.begin(); f2 != F2.end(); ++f2) + { + pkgSrcRecords::File2 f; +#if __GNUC__ >= 4 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + f.MD5Hash = f2->MD5Hash; + f.Size = f2->Size; +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + f.Path = f2->Path; + f.Type = f2->Type; + F.push_back(f); + } + return true; +} +bool debSrcRecordParser::Files2(std::vector &List) +{ + List.clear(); // Stash the / terminated directory prefix string Base = Sect.FindS("Directory"); @@ -133,51 +152,106 @@ bool debSrcRecordParser::Files(std::vector &List) std::vector const compExts = APT::Configuration::getCompressorExtensions(); - // Iterate over the entire list grabbing each triplet - const char *C = Files.c_str(); - while (*C != 0) - { - pkgSrcRecords::File F; - string Size; - - // Parse each of the elements - if (ParseQuoteWord(C,F.MD5Hash) == false || - ParseQuoteWord(C,Size) == false || - ParseQuoteWord(C,F.Path) == false) - return _error->Error("Error parsing file record"); - - // Parse the size and append the directory - F.Size = atoi(Size.c_str()); - F.Path = Base + F.Path; - - // Try to guess what sort of file it is we are getting. - string::size_type Pos = F.Path.length()-1; - while (1) + for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) + { + // derive field from checksum type + std::string checksumField("Checksums-"); + if (strcmp(*type, "MD5Sum") == 0) + checksumField = "Files"; // historic name for MD5 checksums + else + checksumField.append(*type); + + string const Files = Sect.FindS(checksumField.c_str()); + if (Files.empty() == true) + continue; + + // Iterate over the entire list grabbing each triplet + const char *C = Files.c_str(); + while (*C != 0) { - string::size_type Tmp = F.Path.rfind('.',Pos); - if (Tmp == string::npos) - break; - if (F.Type == "tar") { - // source v3 has extension 'debian.tar.*' instead of 'diff.*' - if (string(F.Path, Tmp+1, Pos-Tmp) == "debian") - F.Type = "diff"; - break; - } - F.Type = string(F.Path,Tmp+1,Pos-Tmp); - - if (std::find(compExts.begin(), compExts.end(), std::string(".").append(F.Type)) != compExts.end() || - F.Type == "tar") + string hash, size, path; + + // Parse each of the elements + if (ParseQuoteWord(C, hash) == false || + ParseQuoteWord(C, size) == false || + ParseQuoteWord(C, path) == false) + return _error->Error("Error parsing file record in %s of source package %s", checksumField.c_str(), Package().c_str()); + + HashString const hashString(*type, hash); + if (Base.empty() == false) + path = Base + path; + + // look if we have a record for this file already + std::vector::iterator file = List.begin(); + for (; file != List.end(); ++file) + if (file->Path == path) + break; + + // we have it already, store the new hash and be done + if (file != List.end()) { - Pos = Tmp-1; +#if __GNUC__ >= 4 + // set for compatibility only, so warn users not us + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + if (checksumField == "Files") + file->MD5Hash = hash; +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + // an error here indicates that we have two different hashes for the same file + if (file->Hashes.push_back(hashString) == false) + return _error->Error("Error parsing checksum in %s of source package %s", checksumField.c_str(), Package().c_str()); continue; } - - break; + + // we haven't seen this file yet + pkgSrcRecords::File2 F; + F.Path = path; + F.FileSize = strtoull(size.c_str(), NULL, 10); + F.Hashes.push_back(hashString); + +#if __GNUC__ >= 4 + // set for compatibility only, so warn users not us + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + F.Size = F.FileSize; + if (checksumField == "Files") + F.MD5Hash = hash; +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + + // Try to guess what sort of file it is we are getting. + string::size_type Pos = F.Path.length()-1; + while (1) + { + string::size_type Tmp = F.Path.rfind('.',Pos); + if (Tmp == string::npos) + break; + if (F.Type == "tar") { + // source v3 has extension 'debian.tar.*' instead of 'diff.*' + if (string(F.Path, Tmp+1, Pos-Tmp) == "debian") + F.Type = "diff"; + break; + } + F.Type = string(F.Path,Tmp+1,Pos-Tmp); + + if (std::find(compExts.begin(), compExts.end(), std::string(".").append(F.Type)) != compExts.end() || + F.Type == "tar") + { + Pos = Tmp-1; + continue; + } + + break; + } + List.push_back(F); } - - List.push_back(F); } - + return true; } /*}}}*/ diff --git a/apt-pkg/deb/debsrcrecords.h b/apt-pkg/deb/debsrcrecords.h index b65d1480b..2a3fc86c9 100644 --- a/apt-pkg/deb/debsrcrecords.h +++ b/apt-pkg/deb/debsrcrecords.h @@ -53,6 +53,7 @@ class debSrcRecordParser : public pkgSrcRecords::Parser return std::string(Start,Stop); }; virtual bool Files(std::vector &F); + bool Files2(std::vector &F); debSrcRecordParser(std::string const &File,pkgIndexFile const *Index) : Parser(Index), Fd(File,FileFd::ReadOnly, FileFd::Extension), Tags(&Fd,102400), diff --git a/apt-pkg/srcrecords.cc b/apt-pkg/srcrecords.cc index 81b1c545d..3175ee75f 100644 --- a/apt-pkg/srcrecords.cc +++ b/apt-pkg/srcrecords.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -147,5 +148,33 @@ const char *pkgSrcRecords::Parser::BuildDepType(unsigned char const &Type) return fields[Type]; } /*}}}*/ +bool pkgSrcRecords::Parser::Files2(std::vector &F2)/*{{{*/ +{ + debSrcRecordParser * const deb = dynamic_cast(this); + if (deb != NULL) + return deb->Files2(F2); - + std::vector F; + if (Files(F) == false) + return false; + for (std::vector::const_iterator f = F.begin(); f != F.end(); ++f) + { + pkgSrcRecords::File2 f2; +#if __GNUC__ >= 4 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + f2.MD5Hash = f->MD5Hash; + f2.Size = f->Size; + f2.Hashes.push_back(HashString("MD5Sum", f->MD5Hash)); + f2.FileSize = f->Size; +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + f2.Path = f->Path; + f2.Type = f->Type; + F2.push_back(f2); + } + return true; +} + /*}}}*/ diff --git a/apt-pkg/srcrecords.h b/apt-pkg/srcrecords.h index e000e176a..dde22bd65 100644 --- a/apt-pkg/srcrecords.h +++ b/apt-pkg/srcrecords.h @@ -14,6 +14,7 @@ #define PKGLIB_SRCRECORDS_H #include +#include #include #include @@ -29,15 +30,28 @@ class pkgSrcRecords { public: +#if __GNUC__ >= 4 + // ensure that con- & de-structor don't trigger this warning + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif // Describes a single file struct File { - std::string MD5Hash; - unsigned long Size; + APT_DEPRECATED std::string MD5Hash; + APT_DEPRECATED unsigned long Size; std::string Path; std::string Type; }; - + struct File2 : public File + { + unsigned long long FileSize; + HashStringList Hashes; + }; +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + // Abstract parser for each source record class Parser { @@ -77,6 +91,7 @@ class pkgSrcRecords static const char *BuildDepType(unsigned char const &Type) APT_PURE; virtual bool Files(std::vector &F) = 0; + bool Files2(std::vector &F); Parser(const pkgIndexFile *Index) : iIndex(Index) {}; virtual ~Parser() {}; diff --git a/cmdline/apt-get.cc b/cmdline/apt-get.cc index cfa79339b..a28537712 100644 --- a/cmdline/apt-get.cc +++ b/cmdline/apt-get.cc @@ -797,13 +797,13 @@ static bool DoSource(CommandLine &CmdL) } // Back track - vector Lst; - if (Last->Files(Lst) == false) { + vector Lst; + if (Last->Files2(Lst) == false) { return false; } // Load them into the fetcher - for (vector::const_iterator I = Lst.begin(); + for (vector::const_iterator I = Lst.begin(); I != Lst.end(); ++I) { // Try to guess what sort of file it is we are getting. @@ -832,22 +832,26 @@ static bool DoSource(CommandLine &CmdL) queued.insert(Last->Index().ArchiveURI(I->Path)); // check if we have a file with that md5 sum already localy - if(!I->MD5Hash.empty() && FileExists(flNotDir(I->Path))) - { - FileFd Fd(flNotDir(I->Path), FileFd::ReadOnly); - MD5Summation sum; - sum.AddFD(Fd.Fd(), Fd.Size()); - Fd.Close(); - if((string)sum.Result() == I->MD5Hash) + std::string localFile = flNotDir(I->Path); + if (FileExists(localFile) == true) + if(I->Hashes.VerifyFile(localFile) == true) { ioprintf(c1out,_("Skipping already downloaded file '%s'\n"), - flNotDir(I->Path).c_str()); + localFile.c_str()); continue; } + + // see if we have a hash (Acquire::ForceHash is the only way to have none) + HashString const * const hs = I->Hashes.find(NULL); + if (hs == NULL && _config->FindB("APT::Get::AllowUnauthenticated",false) == false) + { + ioprintf(c1out, "Skipping download of file '%s' as requested hashsum is not available for authentication\n", + localFile.c_str()); + continue; } new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path), - I->MD5Hash,I->Size, + hs != NULL ? hs->toStr() : "", I->FileSize, Last->Index().SourceInfo(*Last,*I),Src); } } diff --git a/debian/libapt-pkg4.12.symbols b/debian/libapt-pkg4.12.symbols index d89f07bb5..d481e51ed 100644 --- a/debian/libapt-pkg4.12.symbols +++ b/debian/libapt-pkg4.12.symbols @@ -1587,6 +1587,8 @@ libapt-pkg.so.4.12 libapt-pkg4.12 #MINVER# (c++)"HashStringList::VerifyFile(std::basic_string, std::allocator >) const@Base" 1.0.9.4 (c++)"HashString::operator==(HashString const&) const@Base" 1.0.9.4 (c++)"HashString::operator!=(HashString const&) const@Base" 1.0.9.4 + (c++)"pkgSrcRecords::Parser::Files2(std::vector >&)@Base" 1.0.9.4 + (c++)"debSrcRecordParser::Files2(std::vector >&)@Base" 1.0.9.4 ### demangle strangeness - buildd report it as MISSING and as new… (c++)"pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire*, std::basic_string, std::allocator >, std::basic_string, std::allocator >, std::basic_string, std::allocator >, std::basic_string, std::allocator >, std::basic_string, std::allocator >, std::basic_string, std::allocator >, std::vector > const*, indexRecords*)@Base" 0.8.0 ### gcc-4.6 artefacts diff --git a/test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum b/test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum new file mode 100755 index 000000000..9bdc81264 --- /dev/null +++ b/test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum @@ -0,0 +1,260 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework + +setupenvironment +configarchitecture 'native' + +cat > aptarchive/Sources < +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-ok_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-ok_1.0.tar.gz + +Package: pkg-sha256-ok +Binary: pkg-sha256-ok +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-ok_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-ok_1.0.tar.gz +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-ok_1.0.dsc + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-ok_1.0.tar.gz +Checksums-Sha256: + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-ok_1.0.dsc + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-ok_1.0.tar.gz + +Package: pkg-sha256-bad +Binary: pkg-sha256-bad +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-bad_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-bad_1.0.tar.gz +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-bad_1.0.dsc + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-bad_1.0.tar.gz +Checksums-Sha256: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 pkg-sha256-bad_1.0.dsc + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 pkg-sha256-bad_1.0.tar.gz + +Package: pkg-no-md5 +Binary: pkg-no-md5 +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-no-md5_1.0.dsc + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-no-md5_1.0.tar.gz +Checksums-Sha256: + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-no-md5_1.0.dsc + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-no-md5_1.0.tar.gz + +Package: pkg-mixed-ok +Binary: pkg-mixed-ok +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-mixed-ok_1.0.tar.gz +Checksums-Sha256: + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-mixed-ok_1.0.dsc + +Package: pkg-mixed-sha1-bad +Binary: pkg-mixed-sha1-bad +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Checksums-Sha1: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 pkg-mixed-sha1-bad_1.0.dsc +Checksums-Sha256: + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-mixed-sha1-bad_1.0.tar.gz + +Package: pkg-mixed-sha2-bad +Binary: pkg-mixed-sha2-bad +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-mixed-sha2-bad_1.0.dsc +Checksums-Sha256: + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 pkg-mixed-sha2-bad_1.0.tar.gz + +Package: pkg-md5-disagree +Binary: pkg-md5-disagree +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-disagree_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-disagree_1.0.tar.gz + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 pkg-md5-disagree_1.0.dsc + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 pkg-md5-disagree_1.0.tar.gz + +Package: pkg-md5-agree +Binary: pkg-md5-agree +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-agree_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-agree_1.0.tar.gz + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-agree_1.0.tar.gz + d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-agree_1.0.dsc + +Package: pkg-sha256-disagree +Binary: pkg-sha256-disagree +Version: 1.0 +Maintainer: Joe Sixpack +Architecture: all +Files: + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-disagree_1.0.dsc + d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-disagree_1.0.tar.gz +Checksums-Sha1: + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-disagree_1.0.dsc + da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-disagree_1.0.tar.gz +Checksums-Sha256: + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-disagree_1.0.dsc + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-disagree_1.0.tar.gz + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 pkg-sha256-disagree_1.0.dsc + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 pkg-sha256-disagree_1.0.tar.gz +EOF + +# create fetchable files +for x in 'pkg-md5-ok' 'pkg-sha256-ok' 'pkg-sha256-bad' 'pkg-no-md5' \ + 'pkg-mixed-ok' 'pkg-mixed-sha1-bad' 'pkg-mixed-sha2-bad' \ + 'pkg-md5-agree' 'pkg-md5-disagree' 'pkg-sha256-disagree'; do + touch aptarchive/${x}_1.0.dsc aptarchive/${x}_1.0.tar.gz +done + +setupaptarchive +changetowebserver +testsuccess aptget update + +testok() { + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz + testequal "Reading package lists... +Building dependency tree... +Need to get 0 B of source archives. +Get:1 http://localhost:8080/ $1 1.0 (dsc) +Get:2 http://localhost:8080/ $1 1.0 (tar) +Download complete and in download only mode" aptget source -d "$@" + msgtest 'Files were successfully downloaded for' "$1" + testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz +} + +testkeep() { + touch ${1}_1.0.dsc ${1}_1.0.tar.gz + testequal "Reading package lists... +Building dependency tree... +Skipping already downloaded file '${1}_1.0.dsc' +Skipping already downloaded file '${1}_1.0.tar.gz' +Need to get 0 B of source archives. +Download complete and in download only mode" aptget source -d "$@" + msgtest 'Files already downloaded are kept for' "$1" + testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz +} + +testmismatch() { + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz + testequal "Reading package lists... +Building dependency tree... +Need to get 0 B of source archives. +Get:1 http://localhost:8080/ $1 1.0 (dsc) +Get:2 http://localhost:8080/ $1 1.0 (tar) +E: Failed to fetch http://localhost:8080/${1}_1.0.dsc Hash Sum mismatch + +E: Failed to fetch http://localhost:8080/${1}_1.0.tar.gz Hash Sum mismatch + +E: Failed to fetch some archives." aptget source -d "$@" + msgtest 'Files were not download as they have hashsum mismatches for' "$1" + testfailure --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz + + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz + testequal "Reading package lists... +Building dependency tree... +Skipping download of file 'pkg-sha256-bad_1.0.dsc' as requested hashsum is not available for authentication +Skipping download of file 'pkg-sha256-bad_1.0.tar.gz' as requested hashsum is not available for authentication +Need to get 0 B of source archives. +Download complete and in download only mode" aptget source -d "$@" -o Acquire::ForceHash=ROT26 + msgtest 'Files were not download as hash is unavailable for' "$1" + testfailure --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz + + rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz + testequal "Reading package lists... +Building dependency tree... +Need to get 0 B of source archives. +Get:1 http://localhost:8080/ $1 1.0 (dsc) +Get:2 http://localhost:8080/ $1 1.0 (tar) +Download complete and in download only mode" aptget source --allow-unauthenticated -d "$@" -o Acquire::ForceHash=ROT26 + msgtest 'Files were downloaded unauthenticated as user allowed it' "$1" + testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz +} + +testok pkg-md5-ok +testkeep pkg-md5-ok +testok pkg-sha256-ok +testkeep pkg-sha256-ok + +# pkg-sha256-bad has a bad SHA sum, but good MD5 sum. If apt is +# checking the best available hash (as it should), this will trigger +# a hash mismatch. +testmismatch pkg-sha256-bad +testmismatch pkg-sha256-bad +testok pkg-sha256-bad -o Acquire::ForceHash=MD5Sum + +# not having MD5 sum doesn't mean the file doesn't exist at all … +testok pkg-no-md5 +testok pkg-no-md5 -o Acquire::ForceHash=SHA256 +testequal "Reading package lists... +Building dependency tree... +Skipping download of file 'pkg-no-md5_1.0.dsc' as requested hashsum is not available for authentication +Skipping download of file 'pkg-no-md5_1.0.tar.gz' as requested hashsum is not available for authentication +Need to get 0 B of source archives. +Download complete and in download only mode" aptget source -d pkg-no-md5 -o Acquire::ForceHash=MD5Sum +msgtest 'Files were not download as MD5 is not available for this package' 'pkg-no-md5' +testfailure --nomsg test -e pkg-no-md5_1.0.dsc -a -e pkg-no-md5_1.0.tar.gz + +# deal with cases in which we haven't for all files the same checksum type +# mostly pathologic as this shouldn't happen, but just to be sure +testok pkg-mixed-ok +testequal 'Reading package lists... +Building dependency tree... +Need to get 0 B of source archives. +Get:1 http://localhost:8080/ pkg-mixed-sha1-bad 1.0 (tar) +Get:2 http://localhost:8080/ pkg-mixed-sha1-bad 1.0 (dsc) +E: Failed to fetch http://localhost:8080/pkg-mixed-sha1-bad_1.0.dsc Hash Sum mismatch + +E: Failed to fetch some archives.' aptget source -d pkg-mixed-sha1-bad +msgtest 'Only tar file is downloaded as the dsc has hashsum mismatch' 'pkg-mixed-sha1-bad' +testsuccess --nomsg test ! -e pkg-mixed-sha1-bad_1.0.dsc -a -e pkg-mixed-sha1-bad_1.0.tar.gz +testequal 'Reading package lists... +Building dependency tree... +Need to get 0 B of source archives. +Get:1 http://localhost:8080/ pkg-mixed-sha2-bad 1.0 (tar) +Get:2 http://localhost:8080/ pkg-mixed-sha2-bad 1.0 (dsc) +E: Failed to fetch http://localhost:8080/pkg-mixed-sha2-bad_1.0.tar.gz Hash Sum mismatch + +E: Failed to fetch some archives.' aptget source -d pkg-mixed-sha2-bad +msgtest 'Only dsc file is downloaded as the tar has hashsum mismatch' 'pkg-mixed-sha2-bad' +testsuccess --nomsg test -e pkg-mixed-sha2-bad_1.0.dsc -a ! -e pkg-mixed-sha2-bad_1.0.tar.gz + +# it gets even more pathologic: multiple entries for one file, some even disagreeing! +testok pkg-md5-agree +testequal 'Reading package lists... +Building dependency tree... +E: Error parsing checksum in Files of source package pkg-md5-disagree' aptget source -d pkg-md5-disagree +testequal 'Reading package lists... +Building dependency tree... +E: Error parsing checksum in Checksums-SHA256 of source package pkg-sha256-disagree' aptget source -d pkg-sha256-disagree -- cgit v1.2.3-70-g09d2 From 50ef3344c3afaaf9943142906b2f976a0337d264 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Fri, 13 Jun 2014 08:35:32 +0200 Subject: deprecate the Section member from package struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A version belongs to a section and has hence a section member of its own. A package on the other hand can have multiple versions from different sections. This was "solved" by using the section which was parsed first as order of sources.list defines, but that is obviously a horribly unpredictable thing. Users are way better of with the Section() as returned by the version they are dealing with. It is likely the same for all versions of a package, but in the few cases it isn't, it is important (like packages moving from main/* to contrib/* or into oldlibs …). Backport of 7a66977 which actually instantly removes the member. --- apt-pkg/cacheiterators.h | 4 +++- apt-pkg/cacheset.h | 11 ++++++++++- apt-pkg/depcache.cc | 2 +- apt-pkg/pkgcache.cc | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h index 2fdf8404d..513f40f17 100644 --- a/apt-pkg/cacheiterators.h +++ b/apt-pkg/cacheiterators.h @@ -160,7 +160,9 @@ class pkgCache::PkgIterator: public Iterator { // Accessors inline const char *Name() const {return S->Name == 0?0:Owner->StrP + S->Name;} - inline const char *Section() const {return S->Section == 0?0:Owner->StrP + S->Section;} + // Versions have sections - and packages can have different versions with different sections + // so this interface is broken by design. Run as fast as you can to Version.Section(). + APT_DEPRECATED inline const char *Section() const {return S->Section == 0?0:Owner->StrP + S->Section;} inline bool Purge() const {return S->CurrentState == pkgCache::State::Purge || (S->CurrentVer == 0 && S->CurrentState == pkgCache::State::NotInstalled);} inline const char *Arch() const {return S->Arch == 0?0:Owner->StrP + S->Arch;} diff --git a/apt-pkg/cacheset.h b/apt-pkg/cacheset.h index 16a3daa42..b7229bc04 100644 --- a/apt-pkg/cacheset.h +++ b/apt-pkg/cacheset.h @@ -118,7 +118,16 @@ public: inline const char *Name() const {return getPkg().Name(); } inline std::string FullName(bool const Pretty) const { return getPkg().FullName(Pretty); } inline std::string FullName() const { return getPkg().FullName(); } - inline const char *Section() const {return getPkg().Section(); } + APT_DEPRECATED inline const char *Section() const { +#if __GNUC__ >= 4 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + return getPkg().Section(); +#if __GNUC__ >= 4 + #pragma GCC diagnostic pop +#endif + } inline bool Purge() const {return getPkg().Purge(); } inline const char *Arch() const {return getPkg().Arch(); } inline pkgCache::GrpIterator Group() const { return getPkg().Group(); } diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 42e31396b..16282df21 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1226,7 +1226,7 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, continue; } // now check if we should consider it a automatic dependency or not - if(InstPkg->CurrentVer == 0 && Pkg->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section())) + if(InstPkg->CurrentVer == 0 && InstVer->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", InstVer.Section())) { if(DebugAutoInstall == true) std::clog << OutputInDepth(Depth) << "Setting NOT as auto-installed (direct " diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc index 58a63459f..d7c9656b9 100644 --- a/apt-pkg/pkgcache.cc +++ b/apt-pkg/pkgcache.cc @@ -524,7 +524,10 @@ operator<<(std::ostream& out, pkgCache::PkgIterator Pkg) out << " -> " << candidate; if ( newest != "none" && candidate != newest) out << " | " << newest; - out << " > ( " << string(Pkg.Section()==0?"none":Pkg.Section()) << " )"; + if (Pkg->VersionList == 0) + out << " > ( none )"; + else + out << " > ( " << string(Pkg.VersionList().Section()==0?"unknown":Pkg.VersionList().Section()) << " )"; return out; } /*}}}*/ -- cgit v1.2.3-70-g09d2 From c505fa33a6441b451971ce6c636cf2ca4dacdc1d Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 28 Sep 2014 01:25:21 +0200 Subject: allow options between command and -- on commandline This used to work before we implemented a stricter commandline parser and e.g. the dd-schroot-cmd command constructs commandlines like this. Reported-By: Helmut Grohne --- apt-pkg/contrib/cmndline.cc | 19 +++++++----- test/libapt/commandline_test.cc | 68 +++++++++++++++++++++++++++++++++++++++++ test/libapt/makefile | 4 +-- 3 files changed, 81 insertions(+), 10 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/cmndline.cc b/apt-pkg/contrib/cmndline.cc index 3799c822d..93c1f4664 100644 --- a/apt-pkg/contrib/cmndline.cc +++ b/apt-pkg/contrib/cmndline.cc @@ -47,23 +47,26 @@ CommandLine::~CommandLine() char const * CommandLine::GetCommand(Dispatch const * const Map, unsigned int const argc, char const * const * const argv) { - // if there is a -- on the line there must be the word we search for around it - // as -- marks the end of the options, just not sure if the command can be - // considered an option or not, so accept both + // if there is a -- on the line there must be the word we search for either + // before it (as -- marks the end of the options) or right after it (as we can't + // decide if the command is actually an option, given that in theory, you could + // have parameters named like commands) for (size_t i = 1; i < argc; ++i) { if (strcmp(argv[i], "--") != 0) continue; - ++i; - if (i < argc) + // check if command is before -- + for (size_t k = 1; k < i; ++k) for (size_t j = 0; Map[j].Match != NULL; ++j) - if (strcmp(argv[i], Map[j].Match) == 0) + if (strcmp(argv[k], Map[j].Match) == 0) return Map[j].Match; - i -= 2; - if (i != 0) + // see if the next token after -- is the command + ++i; + if (i < argc) for (size_t j = 0; Map[j].Match != NULL; ++j) if (strcmp(argv[i], Map[j].Match) == 0) return Map[j].Match; + // we found a --, but not a command return NULL; } // no --, so search for the first word matching a command diff --git a/test/libapt/commandline_test.cc b/test/libapt/commandline_test.cc index e403a28c8..627f1b486 100644 --- a/test/libapt/commandline_test.cc +++ b/test/libapt/commandline_test.cc @@ -2,6 +2,7 @@ #include #include +#include #include @@ -85,3 +86,70 @@ TEST(CommandLineTest, BoolParsing) } } + +bool DoVoid(CommandLine &) { return false; } + +TEST(CommandLineTest,GetCommand) +{ + CommandLine::Dispatch Cmds[] = { {"install",&DoVoid}, {"remove", &DoVoid}, {0,0} }; + { + char const * argv[] = { "apt-get", "-t", "unstable", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector Args = getCommandArgs("apt-get", com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_TRUE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(2, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "foo"); + } + { + char const * argv[] = {"apt-get", "-t", "unstable", "remove", "--", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector Args = getCommandArgs("apt-get", com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(3, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "foo"); + } + { + char const * argv[] = {"apt-get", "-t", "unstable", "--", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector Args = getCommandArgs("apt-get", com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(CmdL.FileSize(), 3); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "foo"); + } + { + char const * argv[] = {"apt-get", "install", "-t", "unstable", "--", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("install", com); + std::vector Args = getCommandArgs("apt-get", com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(CmdL.FileSize(), 4); + EXPECT_EQ(std::string(CmdL.FileList[0]), "install"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[3]), "foo"); + } +} diff --git a/test/libapt/makefile b/test/libapt/makefile index 69a13fd92..7f23ace46 100644 --- a/test/libapt/makefile +++ b/test/libapt/makefile @@ -14,8 +14,8 @@ test: $(BIN)/gtest$(BASENAME) $(BIN)/gtest$(BASENAME): $(LIB)/gtest.a PROGRAM = gtest${BASENAME} -SLIBS = -lapt-pkg -pthread $(LIB)/gtest.a -LIB_MAKES = apt-pkg/makefile +SLIBS = -lapt-pkg -lapt-private -pthread $(LIB)/gtest.a +LIB_MAKES = apt-pkg/makefile apt-private/makefile SOURCE = gtest_runner.cc $(wildcard *-helpers.cc *_test.cc) include $(PROGRAM_H) -- cgit v1.2.3-70-g09d2 From 9fc0b435593839de47098212f0ae5f15b6263099 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 17 Nov 2014 15:06:35 +0100 Subject: close leaking slave fd after setting up pty magic The fd moves out of scope here anyway, so we should close it properly instead of leaking it which will tickle down to dpkg maintainer scripts. Closes: 767774 --- apt-pkg/deb/dpkgpm.cc | 6 +- test/integration/framework | 78 ++++++++++++++++++---- test/integration/test-failing-maintainer-scripts | 46 +------------ .../test-no-fds-leaked-to-maintainer-scripts | 40 +++++++++++ 4 files changed, 109 insertions(+), 61 deletions(-) create mode 100755 test/integration/test-no-fds-leaked-to-maintainer-scripts (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 7bbf18cba..e1f1ec680 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1160,8 +1160,7 @@ void pkgDPkgPM::SetupSlavePtyMagic() int const slaveFd = open(d->slave, O_RDWR); if (slaveFd == -1) _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); - - if (ioctl(slaveFd, TIOCSCTTY, 0) < 0) + else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0) _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd); else { @@ -1172,6 +1171,9 @@ void pkgDPkgPM::SetupSlavePtyMagic() if (tcsetattr(0, TCSANOW, &d->tt) < 0) _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd); } + + if (slaveFd != -1) + close(slaveFd); } void pkgDPkgPM::StopPtyMagic() { diff --git a/test/integration/framework b/test/integration/framework index 7923e23d9..df1942ff9 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -325,6 +325,59 @@ configdpkg() { fi } +configdpkgnoopchroot() { + # create a library to noop chroot() and rewrite maintainer script executions + # via execvp() as used by dpkg as we don't want our rootdir to be a fullblown + # chroot directory dpkg could chroot into to execute the maintainer scripts + msgtest 'Building library to preload to make maintainerscript work in' 'dpkg' + cat << EOF > noopchroot.c +#define _GNU_SOURCE +#include +#include +#include +#include + +static char * chrootdir = NULL; + +int chroot(const char *path) { + printf("WARNING: CHROOTing to %s was ignored!\n", path); + free(chrootdir); + chrootdir = strdup(path); + return 0; +} +int execvp(const char *file, char *const argv[]) { + static int (*func_execvp) (const char *, char * const []) = NULL; + if (func_execvp == NULL) + func_execvp = (int (*) (const char *, char * const [])) dlsym(RTLD_NEXT, "execvp"); + if (chrootdir == NULL || strncmp(file, "/var/lib/dpkg/", strlen("/var/lib/dpkg/")) != 0) + return func_execvp(file, argv); + printf("REWRITE execvp call %s into %s\n", file, chrootdir); + char newfile[strlen(chrootdir) + strlen(file)]; + strcpy(newfile, chrootdir); + strcat(newfile, file); + return func_execvp(newfile, argv); +} +EOF + testsuccess --nomsg gcc -fPIC -shared -o noopchroot.so noopchroot.c -ldl + + mkdir -p "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/" + DPKG="${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" + echo "#!/bin/sh +if [ -n \"\$LD_PRELOAD\" ]; then + export LD_PRELOAD=\"${TMPWORKINGDIRECTORY}/noopchroot.so \${LD_PRELOAD}\" +else + export LD_PRELOAD=\"${TMPWORKINGDIRECTORY}/noopchroot.so\" +fi +dpkg \"\$@\"" > $DPKG + chmod +x $DPKG + sed -ie "s|^DPKG::options:: \"dpkg\";\$|DPKG::options:: \"$DPKG\";|" aptconfig.conf +} + +configallowinsecurerepositories() { + echo "Acquire::AllowInsecureRepositories \"$1\";" > rootdir/etc/apt/apt.conf.d/allow-insecure-repositories.conf + +} + configcompression() { while [ -n "$1" ]; do case "$1" in @@ -442,7 +495,7 @@ buildsimplenativepackage() { fi local BUILDDIR=${TMPWORKINGDIRECTORY}/incoming/${NAME}-${VERSION} - msgninfo "Build package ${NAME} in ${VERSION} for ${RELEASE} in ${DISTSECTION}… " + msgtest "Build source package in version ${VERSION} for ${RELEASE} in ${DISTSECTION}" "$NAME" mkdir -p $BUILDDIR/debian/source echo "* most suckless software product ever" > ${BUILDDIR}/FEATURES echo "#!/bin/sh @@ -474,7 +527,10 @@ Package: $NAME" >> ${BUILDDIR}/debian/control echo "Description: $DESCRIPTION" >> ${BUILDDIR}/debian/control echo '3.0 (native)' > ${BUILDDIR}/debian/source/format - (cd ${BUILDDIR}/..; dpkg-source -b ${NAME}-${VERSION} 2>&1) | sed -n 's#^dpkg-source: info: building [^ ]\+ in ##p' \ + cd ${BUILDDIR}/.. + testsuccess --nomsg dpkg-source -b ${NAME}-${VERSION} + cd - >/dev/null + sed -n 's#^dpkg-source: info: building [^ ]\+ in ##p' ${TMPWORKINGDIRECTORY}/rootdir/tmp/testsuccess.output \ | while read SRC; do echo "pool/${SRC}" >> ${BUILDDIR}/../${RELEASE}.${DISTSECTION}.srclist # if expr match "${SRC}" '.*\.dsc' >/dev/null 2>&1; then @@ -486,6 +542,7 @@ Package: $NAME" >> ${BUILDDIR}/debian/control done for arch in $(getarchitecturesfromcommalist "$ARCH"); do + msgtest "Build binary package for ${RELEASE} in ${SECTION}" "$NAME" rm -rf ${BUILDDIR}/debian/tmp mkdir -p ${BUILDDIR}/debian/tmp/DEBIAN ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME} ${BUILDDIR}/debian/tmp/usr/bin cp ${BUILDDIR}/debian/copyright ${BUILDDIR}/debian/changelog ${BUILDDIR}/FEATURES ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME} @@ -499,11 +556,7 @@ Package: $NAME" >> ${BUILDDIR}/debian/control local LOG="${BUILDDIR}/../${NAME}_${VERSION}_${arch}.dpkg-deb.log" # ensure the right permissions as dpkg-deb ensists chmod 755 ${BUILDDIR}/debian/tmp/DEBIAN - if ! dpkg-deb -Z${COMPRESS_TYPE} --build ${BUILDDIR}/debian/tmp ${BUILDDIR}/.. >$LOG 2>&1; then - cat $LOG - false - fi - rm $LOG + testsuccess --nomsg dpkg-deb -Z${COMPRESS_TYPE} --build ${BUILDDIR}/debian/tmp ${BUILDDIR}/.. echo "pool/${NAME}_${VERSION}_${arch}.deb" >> ${BUILDDIR}/../${RELEASE}.${DISTSECTION}.pkglist done @@ -521,15 +574,13 @@ buildpackage() { local ARCH=$(getarchitecture $4) local PKGNAME="$(echo "$BUILDDIR" | grep -o '[^/]*$')" local BUILDLOG="$(readlink -f "${BUILDDIR}/../${PKGNAME}_${RELEASE}_${SECTION}.dpkg-bp.log")" - msgninfo "Build package ${PKGNAME} for ${RELEASE} in ${SECTION}… " + msgtest "Build package for ${RELEASE} in ${SECTION}" "$PKGNAME" cd $BUILDDIR if [ "$ARCH" = "all" ]; then ARCH="$(dpkg-architecture -qDEB_HOST_ARCH 2> /dev/null)" fi - if ! dpkg-buildpackage -uc -us -a$ARCH >$BUILDLOG 2>&1 ; then - cat $BUILDLOG - false - fi + testsuccess --nomsg dpkg-buildpackage -uc -us -a$ARCH + cp ${TMPWORKINGDIRECTORY}/rootdir/tmp/testsuccess.output $BUILDLOG local PKGS="$(grep '^dpkg-deb: building package' $BUILDLOG | cut -d'/' -f 2 | sed -e "s#'\.##")" local SRCS="$(grep '^dpkg-source: info: building' $BUILDLOG | grep -o '[a-z0-9._+~-]*$')" cd - > /dev/null @@ -539,7 +590,6 @@ buildpackage() { for SRC in $SRCS; do echo "pool/${SRC}" >> ${TMPWORKINGDIRECTORY}/incoming/${RELEASE}.${SECTION}.srclist done - msgdone "info" } buildaptarchive() { @@ -1072,7 +1122,7 @@ testequal() { if [ -n "$MSG" ]; then msgtest "$MSG" "$*" fi - $* 2>&1 | checkdiff $COMPAREFILE - && msgpass || msgfail + "$@" 2>&1 | checkdiff $COMPAREFILE - && msgpass || msgfail } testequalor2() { diff --git a/test/integration/test-failing-maintainer-scripts b/test/integration/test-failing-maintainer-scripts index 3dd7d643e..953506aa5 100755 --- a/test/integration/test-failing-maintainer-scripts +++ b/test/integration/test-failing-maintainer-scripts @@ -6,6 +6,7 @@ TESTDIR=$(readlink -f $(dirname $0)) setupenvironment configarchitecture 'native' +configdpkgnoopchroot # create a bunch of failures createfailure() { @@ -25,51 +26,6 @@ createfailure 'postrm' setupaptarchive -# create a library to noop chroot() and rewrite maintainer script executions -# via execvp() as used by dpkg as we don't want our rootdir to be a fullblown -# chroot directory dpkg could chroot into to execute the maintainer scripts -cat << EOF > noopchroot.c -#define _GNU_SOURCE -#include -#include -#include -#include - -static char * chrootdir = NULL; - -int chroot(const char *path) { - printf("WARNING: CHROOTing to %s was ignored!\n", path); - free(chrootdir); - chrootdir = strdup(path); - return 0; -} -int execvp(const char *file, char *const argv[]) { - static int (*func_execvp) (const char *, char * const []) = NULL; - if (func_execvp == NULL) - func_execvp = (int (*) (const char *, char * const [])) dlsym(RTLD_NEXT, "execvp"); - if (chrootdir == NULL || strncmp(file, "/var/lib/dpkg/", strlen("/var/lib/dpkg/")) != 0) - return func_execvp(file, argv); - printf("REWRITE execvp call %s into %s\n", file, chrootdir); - char newfile[strlen(chrootdir) + strlen(file)]; - strcpy(newfile, chrootdir); - strcat(newfile, file); - return func_execvp(newfile, argv); -} -EOF -testsuccess gcc -fPIC -shared -o noopchroot.so noopchroot.c -ldl - -mkdir -p "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/" -DPKG="${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" -echo "#!/bin/sh -if [ -n \"\$LD_PRELOAD\" ]; then - export LD_PRELOAD=\"${TMPWORKINGDIRECTORY}/noopchroot.so \${LD_PRELOAD}\" -else - export LD_PRELOAD=\"${TMPWORKINGDIRECTORY}/noopchroot.so\" -fi -dpkg \"\$@\"" > $DPKG -chmod +x $DPKG -sed -ie "s|^DPKG::options:: \"dpkg\";\$|DPKG::options:: \"$DPKG\";|" aptconfig.conf - # setup some pre- and post- invokes to check the output isn't garbled later APTHOOK="${TMPWORKINGDIRECTORY}/rootdir/usr/bin/apthook" echo '#!/bin/sh diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts new file mode 100755 index 000000000..6ed120090 --- /dev/null +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -0,0 +1,40 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework + +setupenvironment +configarchitecture 'native' +configdpkgnoopchroot + +setupsimplenativepackage "fdleaks" 'native' '1.0' 'unstable' +BUILDDIR="incoming/fdleaks-1.0" +for script in 'preinst' 'postinst' 'prerm' 'postrm'; do + echo '#!/bin/sh +ls -l /proc/self/fd/' > ${BUILDDIR}/debian/$script +done +buildpackage "$BUILDDIR" 'unstable' 'main' 'native' +rm -rf "$BUILDDIR" + +setupaptarchive + +testsuccess aptget install -y fdleaks +msgtest 'Check if fds were not' 'leaked' +if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '8' ]; then + msgpass +else + echo + cat rootdir/tmp/testsuccess.output + msgfail +fi + +testsuccess aptget purge -y fdleaks +msgtest 'Check if fds were not' 'leaked' +if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '12' ]; then + msgpass +else + echo + cat rootdir/tmp/testsuccess.output + msgfail +fi -- cgit v1.2.3-70-g09d2 From 299aea924ccef428219ed6f1a026c122678429e6 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 18 Nov 2014 00:59:39 +0100 Subject: fix PTY interaction on linux and kfreebsd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We run dpkg on its own pty, so we can log its output and have our own output around it (like the progress bar), while also allowing debconf and configfile prompts to happen. In commit 223ae57d468fdcac451209a095047a07a5698212 we changed to constantly reopening the slave for kfreebsd. This has the sideeffect though that in some cases slave and master will lose their connection on linux, so that no output is passed along anymore. We fix this by having always an fd referencing the slave open (linux), but we don't use it (kfreebsd). Failing to get our PTY up and running has many (bad) consequences including (not limited to, nor all at ones or in any case) garbled ouput, no output, no logging, a (partial) mixture of the previous items, … This commit is therefore also reshuffling quiet a bit of the creation code to get especially the output part up and running on linux and the logging for kfreebsd. Note that the testcase tries to cover some cases, but this is an interactivity issue so only interactive usage can really be a good test. Closes: 765687 --- apt-pkg/deb/dpkgpm.cc | 99 ++++++++++++---------- .../test-no-fds-leaked-to-maintainer-scripts | 40 ++++++++- 2 files changed, 92 insertions(+), 47 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index e1f1ec680..e36a52c3e 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -72,7 +72,8 @@ class pkgDPkgPMPrivate public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), term_out(NULL), history_out(NULL), - progress(NULL), master(-1), slave(NULL) + progress(NULL), tt_is_valid(false), master(-1), + slave(NULL), protect_slave_from_dying(-1) { dpkgbuf[0] = '\0'; } @@ -90,8 +91,10 @@ public: // pty stuff struct termios tt; + bool tt_is_valid; int master; char * slave; + int protect_slave_from_dying; // signals sigset_t sigmask; @@ -1071,47 +1074,40 @@ void pkgDPkgPM::StartPtyMagic() } _error->PushToStack(); - // if tcgetattr for both stdin/stdout returns 0 (no error) - // we do the pty magic - if (tcgetattr(STDOUT_FILENO, &d->tt) == 0 && - tcgetattr(STDIN_FILENO, &d->tt) == 0) + + d->master = posix_openpt(O_RDWR | O_NOCTTY); + if (d->master == -1) + _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); + else if (unlockpt(d->master) == -1) + _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master); + else { - d->master = posix_openpt(O_RDWR | O_NOCTTY); - if (d->master == -1) - _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); - else if (unlockpt(d->master) == -1) - { - _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master); - close(d->master); - d->master = -1; - } + char const * const slave_name = ptsname(d->master); + if (slave_name == NULL) + _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master); else { - char const * const slave_name = ptsname(d->master); - if (slave_name == NULL) + d->slave = strdup(slave_name); + if (d->slave == NULL) + _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master); + else if (grantpt(d->master) == -1) + _error->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name, d->master); + else if (tcgetattr(STDIN_FILENO, &d->tt) == 0) { - _error->Errno("unlockpt", "Getting name for slave of master fd %d failed!", d->master); - close(d->master); - d->master = -1; - } - else - { - d->slave = strdup(slave_name); - if (d->slave == NULL) + d->tt_is_valid = true; + struct termios raw_tt; + // copy window size of stdout if its a 'good' terminal + if (tcgetattr(STDOUT_FILENO, &raw_tt) == 0) { - _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master); - close(d->master); - d->master = -1; + struct winsize win; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) + _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!"); + if (ioctl(d->master, TIOCSWINSZ, &win) < 0) + _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master); } - struct winsize win; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) - _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!"); - if (ioctl(d->master, TIOCSWINSZ, &win) < 0) - _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master); if (tcsetattr(d->master, TCSANOW, &d->tt) == -1) _error->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d->master); - struct termios raw_tt; raw_tt = d->tt; cfmakeraw(&raw_tt); raw_tt.c_lflag &= ~ECHO; @@ -1123,18 +1119,22 @@ void pkgDPkgPM::StartPtyMagic() sigaddset(&d->sigmask, SIGTTOU); sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask); if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1) - _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdout failed!"); + _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!"); sigprocmask(SIG_SETMASK, &d->original_sigmask, NULL); + + } + if (d->slave != NULL) + { + /* on linux, closing (and later reopening) all references to the slave + makes the slave a death end, so we open it here to have one open all + the time. We could use this fd in SetupSlavePtyMagic() for linux, but + on kfreebsd we get an incorrect ("step like") output then while it has + no problem with closing all references… so to avoid platform specific + code here we combine both and be happy once more */ + d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC); } } } - else - { - // complain only if stdout is either a terminal (but still failed) or is an invalid - // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. - if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) - _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); - } if (_error->PendingError() == true) { @@ -1143,17 +1143,23 @@ void pkgDPkgPM::StartPtyMagic() close(d->master); d->master = -1; } + if (d->slave != NULL) + { + free(d->slave); + d->slave = NULL; + } _error->DumpErrors(std::cerr); } _error->RevertToStack(); } void pkgDPkgPM::SetupSlavePtyMagic() { - if(d->master == -1) + if(d->master == -1 || d->slave == NULL) return; if (close(d->master) == -1) _error->FatalE("close", "Closing master %d in child failed!", d->master); + d->master = -1; if (setsid() == -1) _error->FatalE("setsid", "Starting a new session for child failed!"); @@ -1168,7 +1174,7 @@ void pkgDPkgPM::SetupSlavePtyMagic() if (dup2(slaveFd, i) == -1) _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i); - if (tcsetattr(0, TCSANOW, &d->tt) < 0) + if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSANOW, &d->tt) < 0) _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd); } @@ -1180,9 +1186,14 @@ void pkgDPkgPM::StopPtyMagic() if (d->slave != NULL) free(d->slave); d->slave = NULL; + if (d->protect_slave_from_dying != -1) + { + close(d->protect_slave_from_dying); + d->protect_slave_from_dying = -1; + } if(d->master >= 0) { - if (tcsetattr(0, TCSAFLUSH, &d->tt) == -1) + if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSAFLUSH, &d->tt) == -1) _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!"); close(d->master); d->master = -1; diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index 6ed120090..3c6457cab 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -8,7 +8,7 @@ setupenvironment configarchitecture 'native' configdpkgnoopchroot -setupsimplenativepackage "fdleaks" 'native' '1.0' 'unstable' +setupsimplenativepackage "fdleaks" 'all' '1.0' 'unstable' BUILDDIR="incoming/fdleaks-1.0" for script in 'preinst' 'postinst' 'prerm' 'postrm'; do echo '#!/bin/sh @@ -19,7 +19,8 @@ rm -rf "$BUILDDIR" setupaptarchive -testsuccess aptget install -y fdleaks +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +testsuccess aptget install -y fdleaks -qq < /dev/null msgtest 'Check if fds were not' 'leaked' if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '8' ]; then msgpass @@ -29,7 +30,23 @@ else msgfail fi -testsuccess aptget purge -y fdleaks +cp rootdir/tmp/testsuccess.output terminal.output +tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log +testfileequal 'terminal.log' "$(cat terminal.output)" + +testequal 'startup archives unpack +install fdleaks:all 1.0 +status half-installed fdleaks:all 1.0 +status unpacked fdleaks:all 1.0 +status unpacked fdleaks:all 1.0 +startup packages configure +configure fdleaks:all 1.0 +status unpacked fdleaks:all 1.0 +status half-configured fdleaks:all 1.0 +status installed fdleaks:all 1.0' cut -f 3- -d' ' rootdir/var/log/dpkg.log + +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +testsuccess aptget purge -y fdleaks -qq msgtest 'Check if fds were not' 'leaked' if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '12' ]; then msgpass @@ -38,3 +55,20 @@ else cat rootdir/tmp/testsuccess.output msgfail fi +cp rootdir/tmp/testsuccess.output terminal.output +tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log +testfileequal 'terminal.log' "$(cat terminal.output)" + +testequal 'startup packages purge +status installed fdleaks:all 1.0 +remove fdleaks:all 1.0 +status half-configured fdleaks:all 1.0 +status half-installed fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +purge fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +status config-files fdleaks:all 1.0 +status not-installed fdleaks:all ' cut -f 3- -d' ' rootdir/var/log/dpkg.log -- cgit v1.2.3-70-g09d2 From e18f6133b254db9e1dc7b202366b067b15a68123 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 10 Dec 2014 22:26:59 +0100 Subject: do not make PTY slave the controlling terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we have no controlling terminal opening a terminal will make this terminal our controller, which is a serious problem if this happens to be the pseudo terminal we created to run dpkg in as we will close this terminal at the end hanging ourself up in the process… The offending open is the one we do to have at least one slave fd open all the time, but for good measure, we apply the flag also to the slave fd opening in the child process as we set the controlling terminal explicitely here. This is a regression from 150bdc9ca5d656f9fba94d37c5f4f183b02bd746 with the slight twist that this usecase was silently broken before in that it wasn't logging the output in term.log (as a pseudo terminal wasn't created). Closes: 772641 --- apt-pkg/deb/dpkgpm.cc | 4 +- test/integration/framework | 2 +- .../test-no-fds-leaked-to-maintainer-scripts | 109 +++++++++++++-------- 3 files changed, 70 insertions(+), 45 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index e36a52c3e..93a007d14 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1131,7 +1131,7 @@ void pkgDPkgPM::StartPtyMagic() on kfreebsd we get an incorrect ("step like") output then while it has no problem with closing all references… so to avoid platform specific code here we combine both and be happy once more */ - d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC); + d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY); } } } @@ -1163,7 +1163,7 @@ void pkgDPkgPM::SetupSlavePtyMagic() if (setsid() == -1) _error->FatalE("setsid", "Starting a new session for child failed!"); - int const slaveFd = open(d->slave, O_RDWR); + int const slaveFd = open(d->slave, O_RDWR | O_NOCTTY); if (slaveFd == -1) _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0) diff --git a/test/integration/framework b/test/integration/framework index ac482a7a0..9e183057f 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -102,7 +102,7 @@ runapt() { local CMD="$1" shift case $CMD in - sh|aptitude|*/*) ;; + sh|aptitude|*/*|command) ;; *) CMD="${BUILDDIRECTORY}/$CMD";; esac MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY} $CMD "$@" diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index 3c6457cab..6eb033055 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -5,7 +5,7 @@ TESTDIR=$(readlink -f $(dirname $0)) . $TESTDIR/framework setupenvironment -configarchitecture 'native' +configarchitecture 'amd64' 'i386' configdpkgnoopchroot setupsimplenativepackage "fdleaks" 'all' '1.0' 'unstable' @@ -17,58 +17,83 @@ done buildpackage "$BUILDDIR" 'unstable' 'main' 'native' rm -rf "$BUILDDIR" +PKGNAME='fdleaks:all' +if ! dpkg-checkbuilddeps -d 'dpkg (>= 1.16.2)' /dev/null >/dev/null 2>&1; then + PKGNAME='fdleaks' +fi + setupaptarchive rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log testsuccess aptget install -y fdleaks -qq < /dev/null -msgtest 'Check if fds were not' 'leaked' -if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '8' ]; then - msgpass -else - echo - cat rootdir/tmp/testsuccess.output - msgfail -fi -cp rootdir/tmp/testsuccess.output terminal.output -tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log -testfileequal 'terminal.log' "$(cat terminal.output)" +checkfdleak() { + msgtest 'Check if fds were not' 'leaked' + if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = "$1" ]; then + msgpass + else + echo + cat rootdir/tmp/testsuccess.output + msgfail + fi +} +checkinstall() { + checkfdleak 8 + + cp rootdir/tmp/testsuccess.output terminal.output + tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log + testfileequal 'terminal.log' "$(cat terminal.output)" -testequal 'startup archives unpack -install fdleaks:all 1.0 -status half-installed fdleaks:all 1.0 -status unpacked fdleaks:all 1.0 -status unpacked fdleaks:all 1.0 + testequal "startup archives unpack +install $PKGNAME 1.0 +status half-installed $PKGNAME 1.0 +status unpacked $PKGNAME 1.0 +status unpacked $PKGNAME 1.0 startup packages configure -configure fdleaks:all 1.0 -status unpacked fdleaks:all 1.0 -status half-configured fdleaks:all 1.0 -status installed fdleaks:all 1.0' cut -f 3- -d' ' rootdir/var/log/dpkg.log +configure $PKGNAME 1.0 +status unpacked $PKGNAME 1.0 +status half-configured $PKGNAME 1.0 +status installed $PKGNAME 1.0" cut -f 3- -d' ' rootdir/var/log/dpkg.log +} +checkinstall rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log testsuccess aptget purge -y fdleaks -qq -msgtest 'Check if fds were not' 'leaked' -if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = '12' ]; then +checkpurge() { + checkfdleak 12 + + cp rootdir/tmp/testsuccess.output terminal.output + tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log + testfileequal 'terminal.log' "$(cat terminal.output)" + + testequal "startup packages purge +status installed $PKGNAME 1.0 +remove $PKGNAME 1.0 +status half-configured $PKGNAME 1.0 +status half-installed $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +purge $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +status not-installed $PKGNAME " cut -f 3- -d' ' rootdir/var/log/dpkg.log +} +checkpurge + +msgtest 'setsid provided is new enough to support' '-w' +if dpkg-checkbuilddeps -d 'util-linux (>= 2.24.2-1)' /dev/null >/dev/null 2>&1; then msgpass else - echo - cat rootdir/tmp/testsuccess.output - msgfail + msgskip "$(command dpkg -l util-linux)" + exit fi -cp rootdir/tmp/testsuccess.output terminal.output -tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log -testfileequal 'terminal.log' "$(cat terminal.output)" -testequal 'startup packages purge -status installed fdleaks:all 1.0 -remove fdleaks:all 1.0 -status half-configured fdleaks:all 1.0 -status half-installed fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -purge fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -status config-files fdleaks:all 1.0 -status not-installed fdleaks:all ' cut -f 3- -d' ' rootdir/var/log/dpkg.log +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +testsuccess runapt command setsid -w "${BUILDDIRECTORY}/apt-get" install -y fdleaks -qq < /dev/null +checkinstall + +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +testsuccess runapt command setsid -w "${BUILDDIRECTORY}/apt-get" purge -y fdleaks -qq +checkpurge -- cgit v1.2.3-70-g09d2 From a2a75ff4516f7609f4c55b42270abb8d08943c60 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 18 Nov 2014 19:53:56 +0100 Subject: always run 'dpkg --configure -a' at the end of our dpkg callings dpkg checks now for dependencies before running triggers, so that packages can now end up in trigger states (especially those we are not touching at all with our calls) after apt is done running. The solution to this is trivial: Just tell dpkg to configure everything after we have (supposely) configured everything already. In the worst case this means dpkg will have to run a bunch of triggers, usually it will just do nothing though. The code to make this happen was already available, so we just flip a config option here to cause it to be run. This way we can keep pretending that triggers are an implementation detail of dpkg. --triggers-only would supposely work as well, but --configure is more robust in regards to future changes to dpkg and something we will hopefully make use of in future versions anyway (as it was planed at the time this and related options were implemented). Note that dpkg currently has a workaround implemented to allow upgrades to jessie to be clean, so that the test works before and after. Also note that test (compared to the one in the bug) drops the await test as its is considered a loop by dpkg now. Closes: 769609 --- apt-pkg/deb/dpkgpm.cc | 9 ++- test/integration/framework | 25 ++++---- test/integration/test-apt-progress-fd | 67 ++++++++++--------- test/integration/test-apt-progress-fd-deb822 | 18 ++++-- test/integration/test-apt-progress-fd-error | 2 +- ...est-bug-769609-triggers-still-pending-after-run | 75 ++++++++++++++++++++++ .../test-no-fds-leaked-to-maintainer-scripts | 6 +- 7 files changed, 146 insertions(+), 56 deletions(-) create mode 100755 test/integration/test-bug-769609-triggers-still-pending-after-run (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 93a007d14..d54b7b50f 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1047,6 +1047,12 @@ void pkgDPkgPM::BuildPackagesProgressMap() PackagesTotal++; } } + /* one extra: We don't want the progress bar to reach 100%, especially not + if we call dpkg --configure --pending and process a bunch of triggers + while showing 100%. Also, spindown takes a while, so never reaching 100% + is way more correct than reaching 100% while still doing stuff even if + doing it this way is slightly bending the rules */ + ++PackagesTotal; } /*}}}*/ #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13) @@ -1280,9 +1286,8 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress) // support subpressing of triggers processing for special // cases like d-i that runs the triggers handling manually - bool const SmartConf = (_config->Find("PackageManager::Configure", "all") != "all"); bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); - if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true) + if (_config->FindB("DPkg::ConfigurePending", true) == true) List.push_back(Item(Item::ConfigurePending, PkgIterator())); // for the progress diff --git a/test/integration/framework b/test/integration/framework index 9e183057f..c9445065b 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -1178,10 +1178,13 @@ testnopackage() { fi } -testdpkginstalled() { - msgtest "Test for correctly installed package(s) with" "dpkg -l $*" - local PKGS="$(dpkg -l "$@" 2>/dev/null | grep '^i' | wc -l)" - if [ "$PKGS" != $# ]; then +testdpkgstatus() { + local STATE="$1" + local NR="$2" + shift 2 + msgtest "Test that $NR package(s) are in state $STATE with" "dpkg -l $*" + local PKGS="$(dpkg -l "$@" 2>/dev/null | grep "^${STATE}" | wc -l)" + if [ "$PKGS" != $NR ]; then echo >&2 $PKGS dpkg -l "$@" | grep '^[a-z]' >&2 msgfail @@ -1190,16 +1193,12 @@ testdpkginstalled() { fi } +testdpkginstalled() { + testdpkgstatus 'ii' "$#" "$@" +} + testdpkgnotinstalled() { - msgtest "Test for correctly not-installed package(s) with" "dpkg -l $*" - local PKGS="$(dpkg -l "$@" 2> /dev/null | grep '^i' | wc -l)" - if [ "$PKGS" != 0 ]; then - echo - dpkg -l "$@" | grep '^[a-z]' >&2 - msgfail - else - msgpass - fi + testdpkgstatus 'ii' '0' "$@" } testmarkedauto() { diff --git a/test/integration/test-apt-progress-fd b/test/integration/test-apt-progress-fd index d72e7e72d..68cc0439c 100755 --- a/test/integration/test-apt-progress-fd +++ b/test/integration/test-apt-progress-fd @@ -19,13 +19,14 @@ testequal "dlstatus:1:0:Retrieving file 1 of 1 dlstatus:1:0:Retrieving file 1 of 1 pmstatus:dpkg-exec:0:Running dpkg pmstatus:testing:0:Installing testing (amd64) -pmstatus:testing:20:Preparing testing (amd64) -pmstatus:testing:40:Unpacking testing (amd64) -pmstatus:testing:60:Preparing to configure testing (amd64) -pmstatus:dpkg-exec:60:Running dpkg -pmstatus:testing:60:Configuring testing (amd64) -pmstatus:testing:80:Configuring testing (amd64) -pmstatus:testing:100:Installed testing (amd64)" cat apt-progress.log +pmstatus:testing:16.6667:Preparing testing (amd64) +pmstatus:testing:33.3333:Unpacking testing (amd64) +pmstatus:testing:50:Preparing to configure testing (amd64) +pmstatus:dpkg-exec:50:Running dpkg +pmstatus:testing:50:Configuring testing (amd64) +pmstatus:testing:66.6667:Configuring testing (amd64) +pmstatus:testing:83.3333:Installed testing (amd64) +pmstatus:dpkg-exec:83.3333:Running dpkg" cat apt-progress.log # upgrade exec 3> apt-progress.log @@ -34,13 +35,14 @@ testequal "dlstatus:1:0:Retrieving file 1 of 1 dlstatus:1:0:Retrieving file 1 of 1 pmstatus:dpkg-exec:0:Running dpkg pmstatus:testing:0:Installing testing (amd64) -pmstatus:testing:20:Preparing testing (amd64) -pmstatus:testing:40:Unpacking testing (amd64) -pmstatus:testing:60:Preparing to configure testing (amd64) -pmstatus:dpkg-exec:60:Running dpkg -pmstatus:testing:60:Configuring testing (amd64) -pmstatus:testing:80:Configuring testing (amd64) -pmstatus:testing:100:Installed testing (amd64)" cat apt-progress.log +pmstatus:testing:16.6667:Preparing testing (amd64) +pmstatus:testing:33.3333:Unpacking testing (amd64) +pmstatus:testing:50:Preparing to configure testing (amd64) +pmstatus:dpkg-exec:50:Running dpkg +pmstatus:testing:50:Configuring testing (amd64) +pmstatus:testing:66.6667:Configuring testing (amd64) +pmstatus:testing:83.3333:Installed testing (amd64) +pmstatus:dpkg-exec:83.3333:Running dpkg" cat apt-progress.log # reinstall exec 3> apt-progress.log @@ -49,22 +51,24 @@ testequal "dlstatus:1:0:Retrieving file 1 of 1 dlstatus:1:0:Retrieving file 1 of 1 pmstatus:dpkg-exec:0:Running dpkg pmstatus:testing:0:Installing testing (amd64) -pmstatus:testing:20:Preparing testing (amd64) -pmstatus:testing:40:Unpacking testing (amd64) -pmstatus:testing:60:Preparing to configure testing (amd64) -pmstatus:dpkg-exec:60:Running dpkg -pmstatus:testing:60:Configuring testing (amd64) -pmstatus:testing:80:Configuring testing (amd64) -pmstatus:testing:100:Installed testing (amd64)" cat apt-progress.log +pmstatus:testing:16.6667:Preparing testing (amd64) +pmstatus:testing:33.3333:Unpacking testing (amd64) +pmstatus:testing:50:Preparing to configure testing (amd64) +pmstatus:dpkg-exec:50:Running dpkg +pmstatus:testing:50:Configuring testing (amd64) +pmstatus:testing:66.6667:Configuring testing (amd64) +pmstatus:testing:83.3333:Installed testing (amd64) +pmstatus:dpkg-exec:83.3333:Running dpkg" cat apt-progress.log # and remove exec 3> apt-progress.log testsuccess aptget remove testing -y -o APT::Status-Fd=3 testequal "pmstatus:dpkg-exec:0:Running dpkg pmstatus:testing:0:Removing testing (amd64) -pmstatus:testing:33.3333:Preparing for removal of testing (amd64) -pmstatus:testing:66.6667:Removing testing (amd64) -pmstatus:testing:100:Removed testing (amd64)" cat apt-progress.log +pmstatus:testing:25:Preparing for removal of testing (amd64) +pmstatus:testing:50:Removing testing (amd64) +pmstatus:testing:75:Removed testing (amd64) +pmstatus:dpkg-exec:75:Running dpkg" cat apt-progress.log # install non-native and ensure we get proper progress info exec 3> apt-progress.log @@ -75,12 +79,13 @@ testequal "dlstatus:1:0:Retrieving file 1 of 1 dlstatus:1:0:Retrieving file 1 of 1 pmstatus:dpkg-exec:0:Running dpkg pmstatus:testing2:0:Installing testing2 (i386) -pmstatus:testing2:20:Preparing testing2 (i386) -pmstatus:testing2:40:Unpacking testing2 (i386) -pmstatus:testing2:60:Preparing to configure testing2 (i386) -pmstatus:dpkg-exec:60:Running dpkg -pmstatus:testing2:60:Configuring testing2 (i386) -pmstatus:testing2:80:Configuring testing2 (i386) -pmstatus:testing2:100:Installed testing2 (i386)" cat apt-progress.log +pmstatus:testing2:16.6667:Preparing testing2 (i386) +pmstatus:testing2:33.3333:Unpacking testing2 (i386) +pmstatus:testing2:50:Preparing to configure testing2 (i386) +pmstatus:dpkg-exec:50:Running dpkg +pmstatus:testing2:50:Configuring testing2 (i386) +pmstatus:testing2:66.6667:Configuring testing2 (i386) +pmstatus:testing2:83.3333:Installed testing2 (i386) +pmstatus:dpkg-exec:83.3333:Running dpkg" cat apt-progress.log rm -f apt-progress*.log diff --git a/test/integration/test-apt-progress-fd-deb822 b/test/integration/test-apt-progress-fd-deb822 index 9d227942d..badc985e4 100755 --- a/test/integration/test-apt-progress-fd-deb822 +++ b/test/integration/test-apt-progress-fd-deb822 @@ -27,37 +27,41 @@ Message: Installing testing (amd64) Status: progress Package: testing:amd64 -Percent: 20 +Percent: 16.6667 Message: Preparing testing (amd64) Status: progress Package: testing:amd64 -Percent: 40 +Percent: 33.3333 Message: Unpacking testing (amd64) Status: progress Package: testing:amd64 -Percent: 60 +Percent: 50 Message: Preparing to configure testing (amd64) Status: progress -Percent: 60 +Percent: 50 Message: Running dpkg Status: progress Package: testing:amd64 -Percent: 60 +Percent: 50 Message: Configuring testing (amd64) Status: progress Package: testing:amd64 -Percent: 80 +Percent: 66.6667 Message: Configuring testing (amd64) Status: progress Package: testing:amd64 -Percent: 100 +Percent: 83.3333 Message: Installed testing (amd64) + +Status: progress +Percent: 83.3333 +Message: Running dpkg " cat apt-progress.log diff --git a/test/integration/test-apt-progress-fd-error b/test/integration/test-apt-progress-fd-error index a47095b9b..632300765 100755 --- a/test/integration/test-apt-progress-fd-error +++ b/test/integration/test-apt-progress-fd-error @@ -18,7 +18,7 @@ setupaptarchive exec 3> apt-progress.log testfailure aptget install foo1 foo2 -y -o APT::Status-Fd=3 msgtest "Ensure correct error message" -if grep -q "aptarchive/pool/foo2_0.8.15_amd64.deb:40:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log; then +if grep -q "aptarchive/pool/foo2_0.8.15_amd64.deb:36.3636:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log; then msgpass else cat apt-progress.log diff --git a/test/integration/test-bug-769609-triggers-still-pending-after-run b/test/integration/test-bug-769609-triggers-still-pending-after-run new file mode 100755 index 000000000..146fa766b --- /dev/null +++ b/test/integration/test-bug-769609-triggers-still-pending-after-run @@ -0,0 +1,75 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework + +setupenvironment +configarchitecture 'amd64' + +msgtest 'Check if installed dpkg supports' 'noawait trigger' +if dpkg-checkbuilddeps -d 'dpkg (>= 1.16.1)' /dev/null; then + msgpass +else + msgskip 'dpkg version too old' + exit 0 +fi +configdpkgnoopchroot + +buildtriggerpackages() { + local TYPE="$1" + setupsimplenativepackage "triggerable-$TYPE" 'all' '1.0' 'unstable' "Depends: trigdepends-$TYPE" + BUILDDIR="incoming/triggerable-${TYPE}-1.0" + cat >${BUILDDIR}/debian/postinst < ${BUILDDIR}/debian/triggers + buildpackage "$BUILDDIR" 'unstable' 'main' 'native' + rm -rf "$BUILDDIR" + buildsimplenativepackage "trigdepends-$TYPE" 'all' '1.0' 'unstable' +} + +#buildtriggerpackages 'interest' +buildtriggerpackages 'interest-noawait' +buildsimplenativepackage "trigstuff" 'all' '1.0' 'unstable' + +setupaptarchive + +runtests() { + local TYPE="$1" + msgmsg 'Working with trigger type' "$TYPE" + testsuccess aptget install triggerable-$TYPE -y + cp rootdir/tmp/testsuccess.output terminal.output + testsuccess grep '^REWRITE ' terminal.output + testdpkginstalled triggerable-$TYPE trigdepends-$TYPE + + testsuccess aptget install trigdepends-$TYPE -y --reinstall + cp rootdir/tmp/testsuccess.output terminal.output + testsuccess grep '^REWRITE ' terminal.output + testsuccess grep ' root root ' terminal.output + testdpkginstalled triggerable-$TYPE trigdepends-$TYPE + + testsuccess aptget install trigstuff -y + cp rootdir/tmp/testsuccess.output terminal.output + testsuccess grep '^REWRITE ' terminal.output + testsuccess grep ' root root ' terminal.output + testdpkginstalled triggerable-$TYPE trigdepends-$TYPE trigstuff + + testsuccess aptget purge trigstuff -y + cp rootdir/tmp/testsuccess.output terminal.output + testsuccess grep '^REWRITE ' terminal.output + testsuccess grep ' root root ' terminal.output + testdpkginstalled triggerable-$TYPE trigdepends-$TYPE + testdpkgnotinstalled trigstuff + + testsuccess aptget purge trigdepends-$TYPE -y + cp rootdir/tmp/testsuccess.output terminal.output + testfailure grep '^REWRITE ' terminal.output + testfailure grep ' root root ' terminal.output + testdpkgnotinstalled triggerable-$TYPE trigdepends-$TYPE +} +#runtests 'interest' +runtests 'interest-noawait' diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index 6eb033055..7d0c1c6c1 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -53,7 +53,8 @@ startup packages configure configure $PKGNAME 1.0 status unpacked $PKGNAME 1.0 status half-configured $PKGNAME 1.0 -status installed $PKGNAME 1.0" cut -f 3- -d' ' rootdir/var/log/dpkg.log +status installed $PKGNAME 1.0 +startup packages configure" cut -f 3- -d' ' rootdir/var/log/dpkg.log } checkinstall @@ -78,7 +79,8 @@ status config-files $PKGNAME 1.0 status config-files $PKGNAME 1.0 status config-files $PKGNAME 1.0 status config-files $PKGNAME 1.0 -status not-installed $PKGNAME " cut -f 3- -d' ' rootdir/var/log/dpkg.log +status not-installed $PKGNAME +startup packages configure" cut -f 3- -d' ' rootdir/var/log/dpkg.log } checkpurge -- cgit v1.2.3-70-g09d2 From 748a2177dcf8ff72bca90f5c7d516559ddd67352 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 22 Dec 2014 23:14:08 +0100 Subject: pass-through stdin fd instead of content if not a terminal Commit 299aea924ccef428219ed6f1a026c122678429e6 fixes the problem of not logging terminal in case stdin & stdout are not a terminal. The problem is that we are then trying to pass-through stdin content by reading from the apt-process stdin and writing it to the stdin of the child (dpkg), which works great for users who can control themselves, but pipes and co are a bit less forgiving causing us to pass everything to the first child process, which if the sending part of the pipe is e.g. 'yes' we will never see the end of it (as the pipe is full at some point and further writing blocks). There is a simple solution for that of course: If stdin isn't a terminal, we us the apt-process stdin as stdin for the child directly (We don't do this if it is a terminal to be able to save the typed input in the log). Closes: 773061 --- apt-pkg/deb/dpkgpm.cc | 16 ++++++++++++---- .../test-no-fds-leaked-to-maintainer-scripts | 22 ++++++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index d54b7b50f..e23ca466d 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -73,7 +73,8 @@ public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), term_out(NULL), history_out(NULL), progress(NULL), tt_is_valid(false), master(-1), - slave(NULL), protect_slave_from_dying(-1) + slave(NULL), protect_slave_from_dying(-1), + direct_stdin(false) { dpkgbuf[0] = '\0'; } @@ -100,6 +101,7 @@ public: sigset_t sigmask; sigset_t original_sigmask; + bool direct_stdin; }; namespace @@ -1079,6 +1081,9 @@ void pkgDPkgPM::StartPtyMagic() return; } + if (isatty(STDIN_FILENO) == 0) + d->direct_stdin = true; + _error->PushToStack(); d->master = posix_openpt(O_RDWR | O_NOCTTY); @@ -1176,7 +1181,10 @@ void pkgDPkgPM::SetupSlavePtyMagic() _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd); else { - for (unsigned short i = 0; i < 3; ++i) + unsigned short i = 0; + if (d->direct_stdin == true) + ++i; + for (; i < 3; ++i) if (dup2(slaveFd, i) == -1) _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i); @@ -1596,8 +1604,8 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress) // wait for input or output here FD_ZERO(&rfds); - if (d->master >= 0 && !d->stdin_is_dev_null) - FD_SET(0, &rfds); + if (d->master >= 0 && d->direct_stdin == false && d->stdin_is_dev_null == false) + FD_SET(STDIN_FILENO, &rfds); FD_SET(_dpkgin, &rfds); if(d->master >= 0) FD_SET(d->master, &rfds); diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index 7d0c1c6c1..41c057042 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -11,8 +11,14 @@ configdpkgnoopchroot setupsimplenativepackage "fdleaks" 'all' '1.0' 'unstable' BUILDDIR="incoming/fdleaks-1.0" for script in 'preinst' 'postinst' 'prerm' 'postrm'; do - echo '#!/bin/sh -ls -l /proc/self/fd/' > ${BUILDDIR}/debian/$script + cat > ${BUILDDIR}/debian/$script << EOF +#!/bin/sh +if [ -e "$(pwd)/rootdir/tmp/read_stdin" ]; then + read line; + echo "STDIN: -\$line-" +fi +ls -l /proc/self/fd/ +EOF done buildpackage "$BUILDDIR" 'unstable' 'main' 'native' rm -rf "$BUILDDIR" @@ -99,3 +105,15 @@ checkinstall rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log testsuccess runapt command setsid -w "${BUILDDIRECTORY}/apt-get" purge -y fdleaks -qq checkpurge + +touch rootdir/tmp/read_stdin + +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +for i in $(seq 1 10); do echo "$i"; done | testsuccess aptget install -y fdleaks -qq +checkinstall +testequal '2' grep -c '^STDIN: ' rootdir/var/log/apt/term.log + +rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log +yes '' | testsuccess runapt command setsid -w "${BUILDDIRECTORY}/apt-get" purge -y fdleaks -qq +checkpurge +testequal '3' grep -c '^STDIN: ' rootdir/var/log/apt/term.log -- cgit v1.2.3-70-g09d2 From 77b6f202e1629b7794a03b6522d636ff1436d074 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sat, 10 Jan 2015 12:31:18 +0100 Subject: award points for positive dependencies again Commit 9ec748ff103840c4c65471ca00d3b72984131ce4 from Feb 23 last year adds a version check after 8daf68e366fa9fa2794ae667f51562663856237c added 8 days earlier negative points for breaks/conflicts with the intended that only dependencies which are satisfied propagate points (aka: old conflicts do not). The implementation was needlessly complex and flawed through preventing positive dependencies from gaining points like they did before these commits making library transitions harder instead of simpler. It worked out anyhow most of the time out of pure 'luck' (and other ways of gaining points) or got miss attributed to being a temporary hick-up. Closes: 774924 --- apt-pkg/algorithms.cc | 2 +- .../test-allow-scores-for-all-dependency-types | 28 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc index 608ec7fce..b83831053 100644 --- a/apt-pkg/algorithms.cc +++ b/apt-pkg/algorithms.cc @@ -468,7 +468,7 @@ void pkgProblemResolver::MakeScores() if (D->Version != 0) { pkgCache::VerIterator const IV = Cache[T].InstVerIter(Cache); - if (IV.end() == true || D.IsSatisfied(IV) != D.IsNegative()) + if (IV.end() == true || D.IsSatisfied(IV) == false) continue; } Scores[T->ID] += DepMap[D->Type]; diff --git a/test/integration/test-allow-scores-for-all-dependency-types b/test/integration/test-allow-scores-for-all-dependency-types index a5c98f3d6..d60cb8daf 100755 --- a/test/integration/test-allow-scores-for-all-dependency-types +++ b/test/integration/test-allow-scores-for-all-dependency-types @@ -32,6 +32,11 @@ insertpackage 'multipleyes' 'foo' 'amd64' '2.2' 'Conflicts: bar (<= 3)' # having foo multiple times as conflict is a non-advisable hack in general insertpackage 'multipleyes' 'bar' 'amd64' '2.2' 'Conflicts: foo (<= 3), foo (<= 3)' +#774924 - slightly simplified +insertpackage 'jessie' 'login' 'amd64' '2' 'Pre-Depends: libaudit1 (>= 0)' +insertpackage 'jessie' 'libaudit1' 'amd64' '2' 'Depends: libaudit-common (>= 0)' +insertpackage 'jessie' 'libaudit-common' 'amd64' '2' 'Breaks: libaudit0, libaudit1 (<< 2)' + cp rootdir/var/lib/dpkg/status rootdir/var/lib/dpkg/status-backup setupaptarchive @@ -142,3 +147,26 @@ Inst foo [1] (2 versioned [amd64]) Inst baz (2 versioned [amd64]) Conf foo (2 versioned [amd64]) Conf baz (2 versioned [amd64])' aptget install baz -st versioned + +# recreating the exact situation is hard, so we pull tricks to get the score +cp -f rootdir/var/lib/dpkg/status-backup rootdir/var/lib/dpkg/status +insertinstalledpackage 'gdm3' 'amd64' '1' 'Depends: libaudit0, libaudit0' +insertinstalledpackage 'login' 'amd64' '1' 'Essential: yes' +insertinstalledpackage 'libaudit0' 'amd64' '1' +testequal 'Reading package lists... +Building dependency tree... +The following packages will be REMOVED: + gdm3 libaudit0 +The following NEW packages will be installed: + libaudit-common libaudit1 +The following packages will be upgraded: + login +1 upgraded, 2 newly installed, 2 to remove and 0 not upgraded. +Remv gdm3 [1] +Remv libaudit0 [1] +Inst libaudit-common (2 jessie [amd64]) +Conf libaudit-common (2 jessie [amd64]) +Inst libaudit1 (2 jessie [amd64]) +Conf libaudit1 (2 jessie [amd64]) +Inst login [1] (2 jessie [amd64]) +Conf login (2 jessie [amd64])' aptget dist-upgrade -st jessie -- cgit v1.2.3-70-g09d2 From 63b399509d46df71e8d16bd0d362eb23aa6558d7 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 7 Apr 2015 12:20:56 +0200 Subject: Fix crash in pkgDPkgPM::WriteApportReport(() (LP: #1436626) --- apt-pkg/deb/dpkgpm.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index e23ca466d..b187efb40 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1900,8 +1900,19 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) } } - // log the ordering - const char *ops_str[] = {"Install", "Configure","Remove","Purge"}; + // log the ordering, see dpkgpm.h and the "Ops" enum there + const char *ops_str[] = { + "Install", + "Configure", + "Remove", + "Purge", + "ConfigurePending", + "TriggersPending", + "reserved-1", + "reserved-2", + "reserved-3", + "reserved-4", + }; fprintf(report, "AptOrdering:\n"); for (vector::iterator I = List.begin(); I != List.end(); ++I) if ((*I).Pkg != NULL) -- cgit v1.2.3-70-g09d2 From 15901516326737a67f2a9af26cd7e434162de019 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 28 Apr 2015 17:55:00 +0200 Subject: Move sysconf(_SC_OPEN_MAX); out of the for() loop to avoid unneeded syscalls --- apt-pkg/contrib/fileutl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 1ba4674e5..1e6d96fe9 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -778,8 +778,9 @@ pid_t ExecFork(std::set KeepFDs) signal(SIGCONT,SIG_DFL); signal(SIGTSTP,SIG_DFL); + long ScOpenMax = sysconf(_SC_OPEN_MAX); // Close all of our FDs - just in case - for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++) + for (int K = 3; K != ScOpenMax; K++) { if(KeepFDs.find(K) == KeepFDs.end()) fcntl(K,F_SETFD,FD_CLOEXEC); -- cgit v1.2.3-70-g09d2