diff --git a/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.cpp b/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.cpp index f9596cef5..6b1f23e84 100644 --- a/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.cpp +++ b/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.cpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "controlinterface.h" +#include "usbauthorizationutils.h" #include "drivermanager.h" #include "modcore.h" #include "utils.h" @@ -98,15 +99,10 @@ bool ControlInterface::enable(const QString &hclass, const QString &name, const return ioctlEnableNetwork(hclass, name, path, value, enable_device, strDriver); } - // 先从数据库中查找路径,防止设备更换usb接口 - QString sPath = EnableSqlManager::getInstance()->authorizedPath(value); - sPath = path; - // 判断是内置设备,还是外设,内置设备通过remove文件禁用,外设通过authorized文件禁用 bool res = false; - if (QFile::exists("/sys" + sPath + QString("/authorized"))) { - modifyPath(sPath); - res = authorizedEnable(hclass, name, sPath, value, enable_device, strDriver); + if (QFile::exists("/sys" + path + QString("/authorized"))) { + res = authorizedEnable(hclass, name, path, value, enable_device, strDriver); } else { /* if(QFile::exists("/sys" + sPath + QString("/remove")))*/ res = removeEnable(hclass, name, path, value, enable_device, strDriver); } @@ -332,42 +328,14 @@ bool ControlInterface::aptUpdate() #endif bool ControlInterface::authorizedEnable(const QString &hclass, const QString &name, const QString &path, const QString &unique_id, bool enable_device, const QString strDriver) { - // 通过authorized文件启用禁用设备 - // 0:表示禁用 ,1:表示启用 - QFile file("/sys" + path + QString("/authorized")); - if (!file.open(QIODevice::ReadWrite)) { + // A composite USB device may expose keyboard, mouse and control interfaces. + // Keep their authorization state consistent with the selected physical device. + if (!UsbAuthorizationUtils::setInterfacesAuthorized(path, enable_device)) return false; - } - if (enable_device) { - /* - 启用的流程为:以 /devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0 为例 - 第一步: 向 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0/authorized 文件中写 1 - 第二步: 向 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-5/authorized 文件中写 0 - 第三步: 向 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-5/authorized 文件中写 1 - */ - // 第一步 - file.write("1"); - file.close(); - - // 第二步 - QFileInfo fi(path); - QString pop = fi.path(); - QFile fpop("/sys" + pop + QString("/authorized")); - if (!fpop.open(QIODevice::ReadWrite)) - return false; - fpop.write("0"); - fpop.close(); - - // 第三步 - if (!fpop.open(QIODevice::ReadWrite)) - return false; - fpop.write("1"); - fpop.close(); + if (enable_device) { EnableSqlManager::getInstance()->removeDataFromAuthorizedTable(unique_id); } else { - file.write("0"); - file.close(); EnableSqlManager::getInstance()->insertDataToAuthorizedTable(hclass, name, path, unique_id, true, strDriver); } return true; @@ -473,11 +441,6 @@ void ControlInterface::construct_uri(char *buffer, size_t buflen, const char *ba *d = '\0'; } -void ControlInterface::modifyPath(QString &path) -{ - path.replace(QRegExp("[1-9]$"), "0"); -} - void ControlInterface::saveWakeupInfo(const QString &unique_id, const QString &path, bool wakeup) { if (EnableSqlManager::getInstance()->isWakeupUniqueIdExisted(unique_id)) { diff --git a/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.h b/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.h index f2609df2e..90b42f3d7 100644 --- a/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.h +++ b/deepin-devicemanager-server/deepin-devicecontrol/src/controlinterface.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 ~ 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2019-2026 ~ 2023 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -184,13 +184,6 @@ public slots: * @param value */ void construct_uri(char *buffer, size_t buflen, const char *base, const char *value); - /** - * @brief modifyPath 修正路径 保证xxx.0 - * 比如将 /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.1 - * 修正为 /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 - * @param path 需要修正的路径 /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 - */ - void modifyPath(QString &path); /** * @brief saveWakeupInfo 保存数据到数据库 * @param unique_id 唯一标识 diff --git a/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/enableutils.cpp b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/enableutils.cpp index ff43642e9..4f9ca40fa 100644 --- a/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/enableutils.cpp +++ b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/enableutils.cpp @@ -4,6 +4,7 @@ #include "enableutils.h" #include "enablesqlmanager.h" +#include "usbauthorizationutils.h" #include #include @@ -117,9 +118,6 @@ void EnableUtils::disableOutDevice(const QString &info) } else { path = mapItem["SysFS ID"]; } - path.replace(QRegExp("[1-9]$"), "0"); - - // 网卡采用ioctl的方式禁用(链路二:开机/服务唤起时恢复禁用状态) QRegExp reg(REG_ADDRESS); if (reg.exactMatch(uniqueID)) { @@ -133,12 +131,8 @@ void EnableUtils::disableOutDevice(const QString &info) // 先判断设备是否被记录在数据库,如果在则禁用 if (EnableSqlManager::getInstance()->uniqueIDExisted(uniqueID)) { - QFile file("/sys" + path + QString("/authorized")); - if (!file.open(QIODevice::ReadWrite)) { - return; - } - file.write("0"); - file.close(); + if (!UsbAuthorizationUtils::setInterfacesAuthorized(path, false)) + continue; // 更数据库信息,方式更换usb接口 EnableSqlManager::getInstance()->updateDataToAuthorizedTable(uniqueID, path); } diff --git a/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.cpp b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.cpp new file mode 100644 index 000000000..93554abbb --- /dev/null +++ b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.cpp @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "usbauthorizationutils.h" + +#include +#include +#include +#include +#include + +namespace { + +QString physicalDevicePath(const QString &path, const QString &sysRoot) +{ + if (!path.startsWith("/devices/") || path.contains("..")) + return {}; + + const QFileInfo pathInfo(path); + const QString pathName = pathInfo.fileName(); + static const QRegularExpression interfacePattern("^(.+):[0-9]+\\.[0-9]+$"); + static const QRegularExpression devicePattern("^[0-9]+-[0-9]+(?:\\.[0-9]+)*$"); + const QRegularExpressionMatch interfaceMatch = interfacePattern.match(pathName); + + QString deviceName; + QString devicePath; + if (interfaceMatch.hasMatch()) { + deviceName = interfaceMatch.captured(1); + devicePath = pathInfo.path(); + if (QFileInfo(devicePath).fileName() != deviceName) + return {}; + } else { + deviceName = pathName; + devicePath = path; + if (!devicePattern.match(deviceName).hasMatch()) + return {}; + } + + return QDir(sysRoot + devicePath).exists() ? devicePath : QString(); +} + +bool readAuthorized(const QString &filePath, bool &authorized) +{ + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Failed to read USB authorized file" << filePath << file.errorString(); + return false; + } + + const QByteArray value = file.readAll().trimmed(); + if (value != "0" && value != "1") { + qWarning() << "Invalid USB authorized value" << filePath << value; + return false; + } + + authorized = value == "1"; + return true; +} + +bool writeAuthorized(const QString &filePath, bool authorized) +{ + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "Failed to open USB authorized file" << filePath << file.errorString(); + return false; + } + + const QByteArray value = authorized ? "1" : "0"; + if (file.write(value) != value.size()) { + qWarning() << "Failed to write USB authorized file" << filePath << file.errorString(); + return false; + } + + return true; +} + +} // namespace + +QStringList UsbAuthorizationUtils::interfacePaths(const QString &path, const QString &sysRoot) +{ + const QString devicePath = physicalDevicePath(path, sysRoot); + if (devicePath.isEmpty()) + return {}; + + const QString deviceName = QFileInfo(devicePath).fileName(); + QDir deviceDir(sysRoot + devicePath); + const QRegularExpression siblingPattern(QString("^%1:[0-9]+\\.[0-9]+$") + .arg(QRegularExpression::escape(deviceName))); + QStringList interfaces; + const QFileInfoList entries = deviceDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + for (const QFileInfo &entry : entries) { + if (!siblingPattern.match(entry.fileName()).hasMatch()) + continue; + + const QString interfacePath = devicePath + '/' + entry.fileName(); + if (QFileInfo::exists(sysRoot + interfacePath + "/authorized")) + interfaces.append(interfacePath); + } + + return interfaces; +} + +bool UsbAuthorizationUtils::setInterfacesAuthorized(const QString &path, bool authorized, const QString &sysRoot) +{ + const QString devicePath = physicalDevicePath(path, sysRoot); + const QStringList interfaces = interfacePaths(path, sysRoot); + if (devicePath.isEmpty() || interfaces.isEmpty()) { + qWarning() << "No USB interfaces found for" << path; + return false; + } + + QList> originalStates; + for (const QString &interfacePath : interfaces) { + const QString authorizedPath = sysRoot + interfacePath + "/authorized"; + bool originalState = false; + if (!readAuthorized(authorizedPath, originalState)) + return false; + originalStates.append(qMakePair(interfacePath, originalState)); + } + + bool originalDeviceState = false; + const QString deviceAuthorizedPath = sysRoot + devicePath + "/authorized"; + if (authorized && !readAuthorized(deviceAuthorizedPath, originalDeviceState)) + return false; + + int changedCount = 0; + const auto rollbackInterfaces = [&originalStates, &sysRoot, &changedCount]() { + bool restored = true; + for (int index = changedCount - 1; index >= 0; --index) { + const QPair &state = originalStates.at(index); + if (!writeAuthorized(sysRoot + state.first + "/authorized", state.second)) { + qCritical() << "Failed to roll back USB interface" << state.first; + restored = false; + } + } + return restored; + }; + + for (const QString &interfacePath : interfaces) { + if (!writeAuthorized(sysRoot + interfacePath + "/authorized", authorized)) { + if (!rollbackInterfaces()) + qCritical() << "USB interface rollback incomplete for" << path; + return false; + } + ++changedCount; + } + + if (authorized + && (!writeAuthorized(deviceAuthorizedPath, false) + || !writeAuthorized(deviceAuthorizedPath, true))) { + const bool deviceRestored = writeAuthorized(deviceAuthorizedPath, originalDeviceState); + const bool interfacesRestored = rollbackInterfaces(); + if (!deviceRestored || !interfacesRestored) + qCritical() << "USB device rollback incomplete for" << devicePath; + return false; + } + + qInfo() << "Set USB interfaces authorized" << authorized << path << interfaces; + return true; +} diff --git a/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.h b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.h new file mode 100644 index 000000000..c74c0b77c --- /dev/null +++ b/deepin-devicemanager-server/deepin-devicecontrol/src/enablecontrol/usbauthorizationutils.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef USBAUTHORIZATIONUTILS_H +#define USBAUTHORIZATIONUTILS_H + +#include +#include + +class UsbAuthorizationUtils +{ +public: + /** + * @brief Return all authorized-capable interfaces of a physical USB device. + * @param path USB device or interface path below /devices. + * @param sysRoot Sysfs root, overridden by tests. + */ + static QStringList interfacePaths(const QString &path, const QString &sysRoot = "/sys"); + /** + * @brief Set all interfaces to the same authorization state. + * @param path USB device or interface path below /devices. + * @param authorized Whether the interfaces should be authorized. + * @param sysRoot Sysfs root, overridden by tests. + */ + static bool setInterfacesAuthorized(const QString &path, bool authorized, const QString &sysRoot = "/sys"); +}; + +#endif // USBAUTHORIZATIONUTILS_H