Skip to content

修复无法使用未分区U盘制作启动盘的问题#98

Merged
deepin-bot[bot] merged 2 commits intolinuxdeepin:release/eaglefrom
itsXuSt:dev
Nov 7, 2025
Merged

修复无法使用未分区U盘制作启动盘的问题#98
deepin-bot[bot] merged 2 commits intolinuxdeepin:release/eaglefrom
itsXuSt:dev

Conversation

@itsXuSt
Copy link
Contributor

@itsXuSt itsXuSt commented Nov 7, 2025

  • feat: Support creating bootable USB drives from unpartitioned USB disks
  • chore: Update version to 5.7.12

Summary by Sourcery

Enable bootable USB creation from unpartitioned USB drives by treating whole-disk devices as removable media, auto-partitioning them when needed, and enhancing disk enumeration and parsing logic.

New Features:

  • Support auto-creating partitions for unpartitioned USB disks and include them in the device list

Bug Fixes:

  • Fix inability to prepare bootable USB drives from unpartitioned disks

Enhancements:

  • Exclude network file systems during disk scanning and parsing
  • Switch DeviceInfo size fields to 64-bit counters
  • Add verbose debug logging for disk enumeration and parsing

Build:

  • Bump version to 5.7.12

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

TAG Bot

TAG: 5.7.12
EXISTED: no
DISTRIBUTION: unstable

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 7, 2025

Reviewer's Guide

This PR implements support for creating bootable USB drives from unpartitioned USB disks by enhancing device enumeration and parsing routines to detect whole-disk devices, automatically create a partition when needed, and then format it. It also increases disk size precision and bumps the version to 5.7.12.

Sequence diagram for creating a bootable USB from an unpartitioned disk

sequenceDiagram
    participant Installer as QtBaseInstaller
    participant DiskUtil
    participant Device as DeviceInfo
    Installer->Device: Enumerate USB devices
    Device->Installer: Return device list (may include unpartitioned disks)
    Installer->DiskUtil: Check if device is disk (no partition)
    alt Device is disk (no partition)
        Installer->DiskUtil: CreatePartition(targetDev)
        DiskUtil-->>Installer: Partition created
        Installer->DiskUtil: FormatPartion(targetDev + "1")
        DiskUtil-->>Installer: Partition formatted
    else Device is partition
        Installer->DiskUtil: FormatPartion(targetDev)
        DiskUtil-->>Installer: Partition formatted
    end
Loading

Updated class diagram for DeviceInfo with increased precision

classDiagram
    class DeviceInfo {
        QString path
        quint64 used
        quint64 total
        QString label
        QString uuid
        QString fstype
        // ... other members
        DeviceInfo()
        DeviceInfo(const QString &path, quint64 used, quint64 total, const QString &label)
    }
Loading

File-Level Changes

Change Details Files
Enhanced USB device enumeration and parsing to handle unpartitioned disks
  • Added exclusion of network filesystems in df command
  • Inserted debug logs around df, lsblk, and device scanning steps
  • Updated lsblk parsing regex to capture numeric size and convert to MB
  • Scanned /dev/disk/by-id/ to filter USB/MMC devices and exclude network mounts
  • Treated disks without partitions as whole devices, evaluating needFormat and adding to device list
src/libdbm/util/utils.cpp
Automatically create a partition for unpartitioned USB disks before formatting
  • Detected if m_strPartionName refers to a disk node without partition suffix
  • Invoked XSys::DiskUtil::CreatePartition for whole-disk devices
  • Updated target partition name (appended '1') and m_strPartionName before formatting
src/libdbm/installer/qtbaseinstaller.cpp
Increased precision for disk usage fields
  • Changed DeviceInfo.used and DeviceInfo.total from quint32 to quint64
src/libdbm/util/deviceinfo.h
Bumped project version
  • Updated version to 5.7.12 in changelog
debian/changelog

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The manual digit‐based check to distinguish disks from partitions can misclassify eMMC devices like /dev/mmcblk0; consider using a regex (e.g. matching p\d+$) or existing Qt device utilities to reliably detect partition names.
  • There are a large number of qDebug() calls added—consider abstracting these behind a logging level or macro to avoid verbose output in production builds.
  • Instead of the deprecated QRegExp, switch to QRegularExpression for better performance, safety, and clearer syntax when parsing lsblk output.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The manual digit‐based check to distinguish disks from partitions can misclassify eMMC devices like `/dev/mmcblk0`; consider using a regex (e.g. matching `p\d+$`) or existing Qt device utilities to reliably detect partition names.
- There are a large number of qDebug() calls added—consider abstracting these behind a logging level or macro to avoid verbose output in production builds.
- Instead of the deprecated QRegExp, switch to QRegularExpression for better performance, safety, and clearer syntax when parsing lsblk output.

## Individual Comments

### Comment 1
<location> `src/libdbm/util/utils.cpp:327` </location>
<code_context>
+            deviceCount++;
             info.path = reg.cap(1);
             info.label = reg.cap(2);
+            info.total = reg.cap(3).toLongLong() * 1.0 / 1024 / 1024; // MB
             info.uuid = reg.cap(4);
             info.fstype = reg.cap(5);
</code_context>

<issue_to_address>
**suggestion:** Potential loss of precision when converting device size to MB.

Use integer division to avoid unnecessary floating-point operations and ensure type consistency with the field.

```suggestion
            info.total = reg.cap(3).toLongLong() / 1024 / 1024; // MB
```
</issue_to_address>

### Comment 2
<location> `src/libdbm/util/utils.cpp:462-464` </location>
<code_context>
+        QString filePath = usbfileinfoL.at(i).canonicalFilePath();
+
+        // 过滤USB和MMC设备,但排除网络相关设备
+        bool isUsbOrMmc = fileName.contains(QRegExp("^usb-\\S{1,}$")) || fileName.contains(QRegExp("^mmc-\\S{1,}$"));
+        bool isNetwork = fileName.contains(QRegExp("nfs-|cifs-|smbfs-|sshfs-|ftpfs-|davfs-"));
+
+        if (isUsbOrMmc && !isNetwork) {
</code_context>

<issue_to_address>
**suggestion:** Regular expressions may not be optimal for device name filtering.

Prefer QString::startsWith over QRegExp for prefix checks to improve readability and performance.

```suggestion
        // 过滤USB和MMC设备,但排除网络相关设备
        bool isUsbOrMmc = fileName.startsWith("usb-") || fileName.startsWith("mmc-");
        bool isNetwork = fileName.contains(QRegExp("nfs-|cifs-|smbfs-|sshfs-|ftpfs-|davfs-"));
```
</issue_to_address>

### Comment 3
<location> `src/libdbm/util/utils.cpp:573-581` </location>
<code_context>
     outfile.close();
     outfile.remove();
 #endif
+    qDebug() << "Get device list completed: Found" << deviceList.size() << "available devices";
+    for (int i = 0; i < deviceList.size(); ++i) {
+        qDebug() << "  Device" << (i+1) << ":" << deviceList.at(i).path
+                   << "Label:" << deviceList.at(i).label
</code_context>

<issue_to_address>
**suggestion (performance):** Extensive debug logging may impact performance and readability.

Consider making debug logging configurable or reducing verbosity in production to minimize performance impact and log clutter.

```suggestion
    qDebug() << "Get device list completed: Found" << deviceList.size() << "available devices";
    static const bool verboseDebugLogging = false; // Set to true for detailed debug output
    if (verboseDebugLogging) {
        for (int i = 0; i < deviceList.size(); ++i) {
            qDebug() << "  Device" << (i+1) << ":" << deviceList.at(i).path
                       << "Label:" << deviceList.at(i).label
                       << "Filesystem:" << deviceList.at(i).fstype
                       << "Size:" << deviceList.at(i).total << "MB"
                       << "Used:" << deviceList.at(i).used << "MB"
                       << "Need format:" << deviceList.at(i).needFormat;
        }
    }
```
</issue_to_address>

### Comment 4
<location> `src/libdbm/installer/qtbaseinstaller.cpp:380-388` </location>
<code_context>
+        // disk 节点如:/dev/sdb
+        // partition 节点如:/dev/sdb1
+        bool isDisk = true;
+        for (int i = targetDev.length() - 1; i >= 0; i--) {
+            if (targetDev[i].isDigit()) {
+                isDisk = false;
+                break;
+            }
+            if (targetDev[i] == '/') {
+                break;
+            }
</code_context>

<issue_to_address>
**suggestion:** Device type detection logic may not handle all device naming conventions.

Some device names, like /dev/nvme0n1, include digits but are still disks. Using device metadata instead of string patterns would improve reliability.

```suggestion
        // Improved device type detection using device metadata
        auto isDiskDevice = [](const QString& devPath) -> bool {
            QFileInfo fi(devPath);
            QString devName = fi.fileName(); // e.g. "sdb", "sdb1", "nvme0n1"
            QString sysBlockPath = "/sys/class/block/" + devName;
            QDir sysBlockDir(sysBlockPath);
            // If 'device' symlink exists, it's a disk; if not, it's likely a partition
            QFileInfo deviceSymlink(sysBlockPath + "/device");
            return deviceSymlink.exists();
        };

        bool isDisk = isDiskDevice(targetDev);
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 467 to 469
removeDevice.insert(filePath, fileName);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Regular expressions may not be optimal for device name filtering.

Prefer QString::startsWith over QRegExp for prefix checks to improve readability and performance.

Suggested change
// 过滤USB和MMC设备,但排除网络相关设备
bool isUsbOrMmc = fileName.contains(QRegExp("^usb-\\S{1,}$")) || fileName.contains(QRegExp("^mmc-\\S{1,}$"));
bool isNetwork = fileName.contains(QRegExp("nfs-|cifs-|smbfs-|sshfs-|ftpfs-|davfs-"));
// 过滤USB和MMC设备,但排除网络相关设备
bool isUsbOrMmc = fileName.startsWith("usb-") || fileName.startsWith("mmc-");
bool isNetwork = fileName.contains(QRegExp("nfs-|cifs-|smbfs-|sshfs-|ftpfs-|davfs-"));

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Extensive debug logging may impact performance and readability.

Consider making debug logging configurable or reducing verbosity in production to minimize performance impact and log clutter.

Suggested change
qDebug() << "Get device list completed: Found" << deviceList.size() << "available devices";
for (int i = 0; i < deviceList.size(); ++i) {
qDebug() << " Device" << (i+1) << ":" << deviceList.at(i).path
<< "Label:" << deviceList.at(i).label
<< "Filesystem:" << deviceList.at(i).fstype
<< "Size:" << deviceList.at(i).total << "MB"
<< "Used:" << deviceList.at(i).used << "MB"
<< "Need format:" << deviceList.at(i).needFormat;
}
qDebug() << "Get device list completed: Found" << deviceList.size() << "available devices";
static const bool verboseDebugLogging = false; // Set to true for detailed debug output
if (verboseDebugLogging) {
for (int i = 0; i < deviceList.size(); ++i) {
qDebug() << " Device" << (i+1) << ":" << deviceList.at(i).path
<< "Label:" << deviceList.at(i).label
<< "Filesystem:" << deviceList.at(i).fstype
<< "Size:" << deviceList.at(i).total << "MB"
<< "Used:" << deviceList.at(i).used << "MB"
<< "Need format:" << deviceList.at(i).needFormat;
}
}

Comment on lines +380 to +388
bool isDisk = true;
for (int i = targetDev.length() - 1; i >= 0; i--) {
if (targetDev[i].isDigit()) {
isDisk = false;
break;
}
if (targetDev[i] == '/') {
break;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Device type detection logic may not handle all device naming conventions.

Some device names, like /dev/nvme0n1, include digits but are still disks. Using device metadata instead of string patterns would improve reliability.

Suggested change
bool isDisk = true;
for (int i = targetDev.length() - 1; i >= 0; i--) {
if (targetDev[i].isDigit()) {
isDisk = false;
break;
}
if (targetDev[i] == '/') {
break;
}
// Improved device type detection using device metadata
auto isDiskDevice = [](const QString& devPath) -> bool {
QFileInfo fi(devPath);
QString devName = fi.fileName(); // e.g. "sdb", "sdb1", "nvme0n1"
QString sysBlockPath = "/sys/class/block/" + devName;
QDir sysBlockDir(sysBlockPath);
// If 'device' symlink exists, it's a disk; if not, it's likely a partition
QFileInfo deviceSymlink(sysBlockPath + "/device");
return deviceSymlink.exists();
};
bool isDisk = isDiskDevice(targetDev);

- Add automatic partition creation for unpartitioned USB disks using fdisk
- Optimize df command to exclude network filesystems (NFS, CIFS, SMBFS, SSHFS, FTPFS, DAVFS)
- Improve device scanning logic to skip network-related devices

Log: Enable creating bootable USB drives from unpartitioned USB disks and improve disk querying efficiency

Bug: https://pms.uniontech.com/bug-view-338629.html
- Bump version number in debian/changelog
- Add changelog entry for unpartitioned USB support feature

Log: Version update to 5.7.12
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: itsXuSt, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@deepin-ci-robot
Copy link

deepin pr auto review

我来对这个代码变更进行详细审查:

  1. 功能变更分析:
  • 主要新增了对未分区USB磁盘的支持,允许直接使用整个磁盘
  • 优化了设备扫描过程,增加了更多调试信息
  • 改进了磁盘空间大小的数据类型(quint32 -> quint64)
  • 增加了网络文件系统的过滤,避免卡死
  1. 代码质量改进建议:

a) qtbaseinstaller.cpp中的磁盘检测逻辑:

bool isDisk = true;
for (int i = targetDev.length() - 1; i >= 0; i--) {
    if (targetDev[i].isDigit()) {
        isDisk = false;
        break;
    }
    if (targetDev[i] == '/') {
        break;
    }
}

建议改为使用正则表达式或QFileInfo来判断:

QFileInfo devInfo(targetDev);
bool isDisk = !targetDev.endsWith(QRegularExpression("\\d+$"));

b) CreatePartition函数中的分区创建:

QString cmd = QString("echo -e 'o\\nn\\np\\n1\\n\\n\\nw' | fdisk ") + diskDev;

建议使用更安全的QProcess方式:

QProcess fdisk;
fdisk.start("fdisk", QStringList() << diskDev);
fdisk.write("o\nn\np\n1\n\n\nw\n");
fdisk.closeWriteChannel();
fdisk.waitForFinished();

c) 错误处理优化:
在CreatePartition中,建议添加更多的错误检查:

if (!QFileInfo::exists(diskDev)) {
    qCritical() << "Disk device does not exist:" << diskDev;
    return false;
}
  1. 性能优化建议:

a) 设备扫描过程:

  • 建议缓存df和lsblk的输出,避免重复执行
  • 可以考虑使用异步方式执行命令,避免界面卡顿

b) 分区检查:

  • 可以添加超时机制,避免长时间等待
  • 建议添加进度反馈
  1. 安全性改进:

a) 命令执行:

  • 建议对diskDev参数进行验证,防止路径遍历攻击
  • 使用QProcess替代shell命令执行,更安全

b) 权限检查:

  • 建议在执行分区操作前检查用户权限
  • 添加磁盘写保护检查
  1. 其他建议:

a) 日志输出:

  • 建议使用统一的日志级别和格式
  • 可以添加日志文件输出,便于问题追踪

b) 代码结构:

  • 建议将磁盘操作相关的功能抽离为独立类
  • 添加单元测试覆盖新功能

c) 用户反馈:

  • 建议在执行耗时操作时提供进度提示
  • 添加操作取消功能

这些改进建议主要关注代码的健壮性、安全性和用户体验。总体来说,这个变更增加了有用的功能,但在实现细节上还有优化空间。

@itsXuSt
Copy link
Contributor Author

itsXuSt commented Nov 7, 2025

/forcemerge

@deepin-bot
Copy link
Contributor

deepin-bot bot commented Nov 7, 2025

This pr force merged! (status: unstable)

@deepin-bot deepin-bot bot merged commit fad5a43 into linuxdeepin:release/eagle Nov 7, 2025
15 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants