diff --git a/.reuse/dep5 b/.reuse/dep5 index 6730005f..dd534da8 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -45,7 +45,7 @@ Copyright: Curtis Gedak License: GPL-2.0-or-later #config -Files: service/assets/data/* application/assets/deepin-diskmanager.desktop application/assets/environments.h.in service/policy/com.deepin.diskmanager.policy service/udev/99-diskmanager.rules +Files: service/assets/data/* service/assets/dconfig/* application/assets/deepin-diskmanager.desktop application/assets/environments.h.in service/policy/com.deepin.diskmanager.policy service/udev/99-diskmanager.rules Copyright: UnionTech Software Technology Co., Ltd. License: CC0-1.0 diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt index dcd2195e..43e35eac 100644 --- a/service/CMakeLists.txt +++ b/service/CMakeLists.txt @@ -20,9 +20,12 @@ set(APP_USBREMOVE_FILES "${APP_EDEV_DIR}/USBremove.sh") set(APP_DEEPIN_DISKMANAGER_SERVICE_BIN "${APP_EDEV_DIR}/deepin-diskmanager-authenticateProxy") set(APP_POLICY_DIR "policy") set(APP_POLICY_FILES "${APP_POLICY_DIR}/com.deepin.diskmanager.policy") +set(APP_DCONFIG_DIR "${APP_RES_DIR}/dconfig") +set(APP_DCONFIG_SCHEMA "${APP_DCONFIG_DIR}/com.deepin.diskmanager.storage.json") # Find the library find_package(PkgConfig REQUIRED) +find_package(DtkCore REQUIRED) find_package(DtkGui REQUIRED) find_package(Qt5 COMPONENTS Core @@ -79,4 +82,5 @@ install(PROGRAMS ${APP_USBADD_FILES} DESTINATION libexec/openconnect/) install(PROGRAMS ${APP_USBREMOVE_FILES} DESTINATION libexec/openconnect/) install(FILES ${APP_UDEV_FILES} DESTINATION /lib/udev/rules.d/) install(FILES ${APP_POLICY_FILES} DESTINATION share/polkit-1/actions) +install(FILES ${APP_DCONFIG_SCHEMA} DESTINATION share/dsg/configs/com.deepin.diskmanager/) install(PROGRAMS ${APP_DEEPIN_DISKMANAGER_SERVICE_BIN} DESTINATION bin/) diff --git a/service/assets/dconfig/com.deepin.diskmanager.storage.json b/service/assets/dconfig/com.deepin.diskmanager.storage.json new file mode 100644 index 00000000..36c0c8d0 --- /dev/null +++ b/service/assets/dconfig/com.deepin.diskmanager.storage.json @@ -0,0 +1,17 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "ufsSpecVersions": { + "value": ["300", "310", "400", "410"], + "serial": 0, + "flags": ["global"], + "name": "UFS Spec Versions", + "name[zh_CN]": "UFS规范版本", + "description": "Substrings of the UFS spec version read from /sys/block//device/spec_version that should be recognized as the UFS interface. Append new version strings here instead of modifying source code.", + "description[zh_CN]": "从 /sys/block//device/spec_version 读取的UFS规范版本子串列表,匹配任一项即识别为UFS接口。新增UFS版本时只需在此配置中追加,无需修改源码。", + "permissions": "readonly", + "visibility": "private" + } + } +} diff --git a/service/diskoperation/DeviceStorage.cpp b/service/diskoperation/DeviceStorage.cpp index cb3fd798..94c1fb0c 100755 --- a/service/diskoperation/DeviceStorage.cpp +++ b/service/diskoperation/DeviceStorage.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-only @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include "utils.h" namespace DiskManager { @@ -583,6 +586,21 @@ static bool isPGUX() return 1 == isPGUX; } +// 从 DConfig 读取需要识别为 UFS 接口的 spec_version 子串列表,新增 UFS 版本时 +// 只需在 com.deepin.diskmanager.storage 配置中追加,无需修改源码。 +static QStringList ufsSpecVersions() +{ + const QStringList fallback{"300", "310", "400", "410"}; + QScopedPointer cfg( + Dtk::Core::DConfig::create("com.deepin.diskmanager", "com.deepin.diskmanager.storage")); + if (cfg && cfg->isValid()) { + QStringList versions = cfg->value("ufsSpecVersions", QVariant::fromValue(fallback)).toStringList(); + if (!versions.isEmpty()) + return versions; + } + return fallback; +} + void DeviceStorage::getDiskInfoInterface(const QString &devicePath, QString &interface, QString &model) { QString bootDevicePath("/proc/bootdevice/product_name"); @@ -592,8 +610,11 @@ void DeviceStorage::getDiskInfoInterface(const QString &devicePath, QString &int if (model == file.readLine().simplified()) { QString spec_version = Utils::readContent("/sys/block/sdd/device/spec_version").trimmed(); if (!spec_version.isEmpty()) { - if (spec_version.contains("300") || spec_version.contains("310") || spec_version.contains("400")) { - interface = "UFS"; + foreach (const QString &version, ufsSpecVersions()) { + if (spec_version.contains(version)) { + interface = "UFS"; + break; + } } } } @@ -606,7 +627,9 @@ void DeviceStorage::getDiskInfoInterface(const QString &devicePath, QString &int proc.start(cmd); proc.waitForFinished(-1); QString outPut = proc.readAllStandardOutput().trimmed(); - QStringList outPutList = outPut.split("("); + QMap mapInfo; + getMapInfoFromInput(outPut, mapInfo); + QStringList outPutList = mapInfo.value("Attached to").split("("); interface = outPutList[outPutList.size() - 1].split(" ")[0]; } return; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1536473e..d9803cb7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,9 +25,11 @@ LIST(APPEND SRC_LIST ${UT_DISKOPERATION}) file(GLOB ALL_HEADERS "../service/diskoperation/*.h" "../service/diskoperation/filesystems/*.h") file(GLOB ALL_SOURCES "../service/diskoperation/*.cpp" "../service/diskoperation/filesystems/*.cpp") +find_package(DtkCore REQUIRED) + add_executable(${PROJECT_NAME_TEST} ${SRC_LIST} ${ALL_HEADERS} ${ALL_SOURCES}) -target_link_libraries(${PROJECT_NAME_TEST} gmock gmock_main gtest gtest_main pthread Qt5::Core basestruct parted parted-fs-resize) +target_link_libraries(${PROJECT_NAME_TEST} gmock gmock_main gtest gtest_main pthread Qt5::Core ${DtkCore_LIBRARIES} basestruct parted parted-fs-resize) # 添加 QTest 测试 add_test(${PROJECT_NAME_TEST} that-test-I-made COMMAND ${PROJECT_NAME_TEST}) diff --git a/test/ut_diskoperation/ut_devicestorage.cpp b/test/ut_diskoperation/ut_devicestorage.cpp new file mode 100644 index 00000000..df126a60 --- /dev/null +++ b/test/ut_diskoperation/ut_devicestorage.cpp @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-only + +#include "gtest/gtest.h" + +#include "../../service/diskoperation/DeviceStorage.h" + +#include +#include + +using namespace DiskManager; + +namespace { + +class PathGuard +{ +public: + explicit PathGuard(const QString &path) + : m_oldPath(qgetenv("PATH")) + { + qputenv("PATH", QString("%1:%2").arg(path, QString::fromLocal8Bit(m_oldPath)).toLocal8Bit()); + } + + ~PathGuard() + { + qputenv("PATH", m_oldPath); + } + +private: + QByteArray m_oldPath; +}; + +void writeExecutable(const QString &filePath, const QByteArray &content) +{ + QFile file(filePath); + ASSERT_TRUE(file.open(QIODevice::WriteOnly | QIODevice::Truncate)); + ASSERT_EQ(file.write(content), content.size()); + file.close(); + ASSERT_TRUE(file.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner)); +} + +} // namespace + +class ut_devicestorage : public ::testing::Test +{ +}; + +TEST_F(ut_devicestorage, getDiskInfoInterface_usesAttachedToWhenCapacityFollows) +{ + QTemporaryDir dir; + ASSERT_TRUE(dir.isValid()); + writeExecutable(dir.filePath("hwinfo"), + "#!/bin/sh\n" + "printf '%s\\n' '10: None 00.0: 10600 Disk' " + "' Attached to: #1 (UFS 3.1 Controller)' " + "' Capacity: 1 TB (1024626524160 bytes)'\n"); + PathGuard guard(dir.path()); + + DeviceStorage storage; + QString interface; + QString model("__ut_nonexistent_model__"); + + storage.getDiskInfoInterface("/dev/__ut_nonexistent_pms372777_ufs__", interface, model); + + EXPECT_EQ(interface, "UFS"); +} + +TEST_F(ut_devicestorage, getDiskInfoInterface_ignoresCapacityBytesWhenInterfaceMissing) +{ + QTemporaryDir dir; + ASSERT_TRUE(dir.isValid()); + writeExecutable(dir.filePath("hwinfo"), + "#!/bin/sh\n" + "printf '%s\\n' ' Capacity: 1 TB (1024626524160 bytes)'\n"); + PathGuard guard(dir.path()); + + DeviceStorage storage; + QString interface; + QString model("__ut_nonexistent_model__"); + + storage.getDiskInfoInterface("/dev/__ut_nonexistent_pms372777_capacity__", interface, model); + + EXPECT_TRUE(interface.isEmpty()); +}