From c048aecb4858286a1516dba50317b4568f0d5c3d Mon Sep 17 00:00:00 2001 From: Arttu Tarkiainen Date: Wed, 22 Dec 2021 11:54:43 +0200 Subject: Do not replace conflicting remote packages silently with locals On (tree)name conflicts with other than the same component in local and remote packages - instead of silently replacing the remote package with local, do one of the following: - Remote package A may have declared the same tree name as installed local package B. Catch the case and re-register package A as an unstable component with the original name, if possible. - Remote package A may have declared a tree name that conflicts with local package B's name. Do not register component A. Change-Id: I16ac583ea10bf203d8b8d0b19e6890f03cf0067a Reviewed-by: Katja Marttila --- src/libs/installer/packagemanagercore.cpp | 43 +++++++++++++++-- tests/auto/installer/treename/data/components.xml | 59 +++++++++++++++++++++++ tests/auto/installer/treename/settings.qrc | 2 + tests/auto/installer/treename/tst_treename.cpp | 59 +++++++++++++++++++++++ 4 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 tests/auto/installer/treename/data/components.xml diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index bb2eca6e6..81c0bea3a 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -3909,14 +3909,47 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc } else if (remoteTreeNameComponents.contains(localComponent->name())) { const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name()); const QString localTreeName = localComponent->value(scTreeName); - if (remoteTreeName != localTreeName) + if (remoteTreeName != localTreeName) { delete allComponents.take(remoteTreeNameComponents.value(localComponent->name())); - else + } else { + // 3. Component has same treename in local and remote, don't add the component again. continue; - // 3. Component has same treename in local and remote, don't add the component again. - } else if (allComponents.contains(name)) { - continue; + } + // 4. Component does not have treename in local or remote, don't add the component again. + } else if (allComponents.contains(localComponent->name())) { + Component *const component = allComponents.value(localComponent->name()); + if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty()) + continue; + } + // 5. Remote has treename for a different component that is already reserved + // by this local component, Or, remote adds component without treename + // but it conflicts with a local treename. + if (allComponents.contains(name)) { + const QString key = remoteTreeNameComponents.key(name); + qCritical() << "Cannot register component" << (key.isEmpty() ? name : key) + << "with name" << name << "! Component with identifier" << name + << "already exists."; + + if (!key.isEmpty()) + allTreeNameComponents.remove(key); + + // Try to re-add the remote component as unstable + if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) { + qCDebug(lcInstallerInstallLog) + << "Registering component with the original indetifier:" << key; + + Component *component = allComponents.take(name); + component->removeValue(scTreeName); + const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier"); + d->m_pendingUnstableComponents.insert(component->name(), + QPair(Component::InvalidTreeName, errorString)); + + allComponents.insert(key, component); + } else { + delete allComponents.take(name); + } } + const QString treeName = localComponent->value(scTreeName); if (!treeName.isEmpty()) allTreeNameComponents.insert(localComponent->name(), treeName); diff --git a/tests/auto/installer/treename/data/components.xml b/tests/auto/installer/treename/data/components.xml new file mode 100644 index 000000000..0cb0e9826 --- /dev/null +++ b/tests/auto/installer/treename/data/components.xml @@ -0,0 +1,59 @@ + + Test Application + 1.0.0 + + ASub2ToRoot + Root component 1 + A package without treename (remote treename conflicts with local name) + + 1.0.0-1 + + 2021-01-01 + 0 + true + + + B + Root component 2 + A package with treename (remote treename conflicts with local treename) + BSub2ToRoot + 1.0.0-1 + + 2021-01-01 + 0 + true + + + componentB.sub1.sub2 + Sub component + A package with treename (remote has different treename) + componentC.sub2 + 1.0.0-1 + + 2021-01-01 + 0 + true + + + C + Root component 3 + A package with treename (remote name conflicts with local treename) + componentA + 1.0.0-1 + + 2021-01-01 + 0 + true + + + componentD + Root component 4 + A package with treename (remote does not have treename) + componentDNew + 1.0.0-1 + + 2021-01-01 + 0 + true + + diff --git a/tests/auto/installer/treename/settings.qrc b/tests/auto/installer/treename/settings.qrc index eab771bee..9b31cbade 100644 --- a/tests/auto/installer/treename/settings.qrc +++ b/tests/auto/installer/treename/settings.qrc @@ -1,5 +1,7 @@ + data/components.xml + data/repository/Updates.xml data/repository/componentA/1.0.0meta.7z data/repository/componentA/1.0.0content.7z diff --git a/tests/auto/installer/treename/tst_treename.cpp b/tests/auto/installer/treename/tst_treename.cpp index 974bc08cc..3d42126eb 100644 --- a/tests/auto/installer/treename/tst_treename.cpp +++ b/tests/auto/installer/treename/tst_treename.cpp @@ -59,6 +59,8 @@ private slots: void replaceComponentWithTreeName(); void replaceComponentWithTreeNameMoveChildren(); + void remotePackageConflictsLocal(); + void init(); void cleanup(); @@ -381,6 +383,63 @@ void tst_TreeName::autoDependOnMovedSubItem() << "installcontentA.txt" << "installcontentA_1.txt" << "installcontentB.txt"); } +void tst_TreeName::remotePackageConflictsLocal() +{ + const QString packageHubFile = qApp->applicationDirPath() + QDir::separator() + "components.xml"; + QFile::remove(packageHubFile); + QVERIFY(QFile::copy(":///data/components.xml", packageHubFile)); + // For some reason Windows sets the read-only flag when we copy the resource.. + QVERIFY(setDefaultFilePermissions(packageHubFile, DefaultFilePermissions::NonExecutable)); + + QHash params; + params.insert(scTargetDir, qApp->applicationDirPath()); + PackageManagerCore core(BinaryContent::MagicPackageManagerMarker, QList(), + QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, params); + + core.settings().setAllowUnstableComponents(true); + core.settings().setDefaultRepositories(QSet() + << Repository::fromUserInput(":///data/repository")); + + QVERIFY(core.fetchRemotePackagesTree()); + { + // Remote treename conflicts with local name + Component *const local = core.componentByName("ASub2ToRoot"); + QVERIFY(local && local->isInstalled()); + + Component *const remote = core.componentByName("componentA.sub2"); + QVERIFY(remote && remote->isUnstable()); + QCOMPARE(remote->treeName(), remote->name()); + } + { + // Remote treename conflicts with local treename + Component *const local = core.componentByName("B"); + QVERIFY(local && local->isInstalled() && local->treeName() == "BSub2ToRoot"); + + Component *const remote = core.componentByName("componentB.sub2"); + QVERIFY(remote && remote->isUnstable()); + QCOMPARE(remote->treeName(), remote->name()); + } + { + // Remote name conflicts with local treename + Component *const local = core.componentByName("C"); + QVERIFY(local && local->isInstalled() && local->treeName() == "componentA"); + + Component *const remote = core.componentByName("componentA"); + QVERIFY(!remote); + } + { + // Component has a treename in local but not in remote, add with local treename + Component *const component = core.componentByName("componentD"); + QVERIFY(component && component->isInstalled() && component->treeName() == "componentDNew"); + } + { + // Component has different treename in local and remote, add with local treename + Component *const component = core.componentByName("componentB.sub1.sub2"); + QVERIFY(component && component->isInstalled() && component->treeName() == "componentC.sub2"); + } + QVERIFY(QFile::remove(packageHubFile)); +} + void tst_TreeName::init() { m_installDir = QInstaller::generateTemporaryFileName(); -- cgit v1.2.3