|
|
@@ -22,6 +22,7 @@
|
|
|
#include <apt-pkg/cacheiterators.h>
|
|
|
#include <apt-pkg/macros.h>
|
|
|
#include <apt-pkg/pkgcache.h>
|
|
|
+#include <apt-pkg/version.h>
|
|
|
|
|
|
#include <errno.h>
|
|
|
#include <fcntl.h>
|
|
|
@@ -36,6 +37,8 @@
|
|
|
#include <sys/stat.h>
|
|
|
#include <sys/time.h>
|
|
|
#include <sys/wait.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <dirent.h>
|
|
|
#include <termios.h>
|
|
|
#include <time.h>
|
|
|
#include <unistd.h>
|
|
|
@@ -1221,6 +1224,34 @@ void pkgDPkgPM::StopPtyMagic() /*{{{*/
|
|
|
d->master = -1;
|
|
|
}
|
|
|
}
|
|
|
+ /*}}}*/
|
|
|
+static void cleanUpTmpDir(char * const tmpdir) /*{{{*/
|
|
|
+{
|
|
|
+ if (tmpdir == nullptr)
|
|
|
+ return;
|
|
|
+ DIR * const D = opendir(tmpdir);
|
|
|
+ if (D == nullptr)
|
|
|
+ _error->Errno("opendir", _("Unable to read %s"), tmpdir);
|
|
|
+ else
|
|
|
+ {
|
|
|
+ auto const dfd = dirfd(D);
|
|
|
+ for (struct dirent *Ent = readdir(D); Ent != nullptr; Ent = readdir(D))
|
|
|
+ {
|
|
|
+ if (Ent->d_name[0] == '.')
|
|
|
+ continue;
|
|
|
+#ifdef _DIRENT_HAVE_D_TYPE
|
|
|
+ if (unlikely(Ent->d_type != DT_LNK && Ent->d_type != DT_UNKNOWN))
|
|
|
+ continue;
|
|
|
+#endif
|
|
|
+ if (unlikely(unlinkat(dfd, Ent->d_name, 0) != 0))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ closedir(D);
|
|
|
+ rmdir(tmpdir);
|
|
|
+ }
|
|
|
+ free(tmpdir);
|
|
|
+}
|
|
|
+ /*}}}*/
|
|
|
|
|
|
// DPkgPM::Go - Run the sequence /*{{{*/
|
|
|
// ---------------------------------------------------------------------
|
|
|
@@ -1276,6 +1307,19 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
if (noopDPkgInvocation == false)
|
|
|
Cache.writeStateFile(NULL);
|
|
|
|
|
|
+ bool dpkg_recursive_install = _config->FindB("dpkg::install::recursive", false);
|
|
|
+ if (_config->FindB("dpkg::install::recursive::force", false) == false)
|
|
|
+ {
|
|
|
+ // dpkg uses a sorted treewalk since that version which enables the workaround to work
|
|
|
+ auto const dpkgpkg = Cache.FindPkg("dpkg");
|
|
|
+ if (likely(dpkgpkg.end() == false && dpkgpkg->CurrentVer != 0))
|
|
|
+ dpkg_recursive_install = Cache.VS().CmpVersion("1.18.5", dpkgpkg.CurrentVer().VerStr()) <= 0;
|
|
|
+ }
|
|
|
+ // no point in doing this dance for a handful of packages only
|
|
|
+ unsigned int const dpkg_recursive_install_min = _config->FindB("dpkg::install::recursive::minimum", 5);
|
|
|
+ // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
|
|
|
+ bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true);
|
|
|
+
|
|
|
decltype(List)::const_iterator::difference_type const notconfidx =
|
|
|
_config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits<decltype(notconfidx)>::max() :
|
|
|
std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base());
|
|
|
@@ -1396,17 +1440,47 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
{
|
|
|
ADDARGC("--no-triggers");
|
|
|
}
|
|
|
-#undef ADDARGC
|
|
|
+ char * tmpdir_to_free = nullptr;
|
|
|
|
|
|
// Write in the file or package names
|
|
|
if (I->Op == Item::Install)
|
|
|
{
|
|
|
- for (;I != J && Size < MaxArgBytes; ++I)
|
|
|
+ auto const installsToDo = J - I;
|
|
|
+ if (dpkg_recursive_install == true && dpkg_recursive_install_min < installsToDo)
|
|
|
{
|
|
|
- if (I->File[0] != '/')
|
|
|
- return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
|
|
|
- Args.push_back(I->File.c_str());
|
|
|
- Size += I->File.length();
|
|
|
+ std::string tmpdir;
|
|
|
+ strprintf(tmpdir, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
|
|
|
+ tmpdir_to_free = strndup(tmpdir.data(), tmpdir.length());
|
|
|
+ if (mkdtemp(tmpdir_to_free) == nullptr)
|
|
|
+ return _error->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free);
|
|
|
+
|
|
|
+ char p = 1;
|
|
|
+ for (auto c = installsToDo - 1; (c = c/10) != 0; ++p);
|
|
|
+ for (unsigned long n = 0; I != J; ++n, ++I)
|
|
|
+ {
|
|
|
+ if (I->File[0] != '/')
|
|
|
+ return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
|
|
|
+ auto const file = flNotDir(I->File);
|
|
|
+ std::string linkpath;
|
|
|
+ if (dpkg_recursive_install_numbered)
|
|
|
+ strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str());
|
|
|
+ else
|
|
|
+ strprintf(linkpath, "%s/%s", tmpdir_to_free, file.c_str());
|
|
|
+ if (symlink(I->File.c_str(), linkpath.c_str()) != 0)
|
|
|
+ return _error->Errno("DPkg::Go", "Symlinking %s to %s failed!", I->File.c_str(), linkpath.c_str());
|
|
|
+ }
|
|
|
+ ADDARGC("--recursive");
|
|
|
+ ADDARG(tmpdir_to_free);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (;I != J && Size < MaxArgBytes; ++I)
|
|
|
+ {
|
|
|
+ if (I->File[0] != '/')
|
|
|
+ return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
|
|
|
+ Args.push_back(I->File.c_str());
|
|
|
+ Size += I->File.length();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
@@ -1454,6 +1528,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
if (oldSize == Size)
|
|
|
continue;
|
|
|
}
|
|
|
+#undef ADDARGC
|
|
|
#undef ADDARG
|
|
|
|
|
|
J = I;
|
|
|
@@ -1470,6 +1545,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
Packages.clear();
|
|
|
close(fd[0]);
|
|
|
close(fd[1]);
|
|
|
+ cleanUpTmpDir(tmpdir_to_free);
|
|
|
continue;
|
|
|
}
|
|
|
Args.push_back(NULL);
|
|
|
@@ -1605,6 +1681,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
signal(SIGINT,old_SIGINT);
|
|
|
signal(SIGHUP,old_SIGHUP);
|
|
|
|
|
|
+ cleanUpTmpDir(tmpdir_to_free);
|
|
|
+
|
|
|
if (waitpid_failure == true)
|
|
|
{
|
|
|
strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
|
|
|
@@ -1775,7 +1853,17 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
|
|
|
// find the package version and source package name
|
|
|
pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
|
|
|
if (Pkg.end() == true)
|
|
|
- return;
|
|
|
+ {
|
|
|
+ if (pos == std::string::npos || _config->FindB("dpkg::install::recursive::numbered", true) == false)
|
|
|
+ return;
|
|
|
+ auto const dash = pkgname.find_first_not_of("0123456789");
|
|
|
+ if (dash == std::string::npos || pkgname[dash] != '-')
|
|
|
+ return;
|
|
|
+ pkgname.erase(0, dash + 1);
|
|
|
+ Pkg = Cache.FindPkg(pkgname);
|
|
|
+ if (Pkg.end() == true)
|
|
|
+ return;
|
|
|
+ }
|
|
|
pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
|
|
|
if (Ver.end() == true)
|
|
|
return;
|