Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
deepin-boot-maker (5.7.12) unstable; urgency=medium

* Support creating bootable USB drives from unpartitioned USB disks

-- XuShitong <xushitong@uniontech.com> Fri, 07 Nov 2025 16:11:17 +0800

deepin-boot-maker (5.7.11) unstable; urgency=medium

* a permission promote issue was resolved.
Expand Down
32 changes: 31 additions & 1 deletion src/libdbm/installer/qtbaseinstaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,37 @@ bool QtBaseInstaller::formatUsb()
}

if (m_bFormat) {
if (!XSys::DiskUtil::FormatPartion(m_strPartionName)) {
QString targetDev = m_strPartionName;

// 检查是否是 disk 节点(没有分区号)
// 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;
}
Comment on lines +380 to +388
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);

}

// 如果是 disk 节点,需要先创建分区
if (isDisk) {
qDebug() << "Detected disk device" << targetDev << "without partition, creating partition...";
if (!XSys::DiskUtil::CreatePartition(targetDev)) {
qCritical() << "Failed to create partition for" << targetDev;
return false;
}
// 使用新创建的分区
targetDev = targetDev + "1";
m_strPartionName = targetDev;
qDebug() << "Partition created successfully, using" << targetDev;
}

// 格式化分区
if (!XSys::DiskUtil::FormatPartion(targetDev)) {
return false;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/libdbm/util/deviceinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class DeviceInfo
{
public:
DeviceInfo() {}
DeviceInfo(const QString &path, quint32 used, quint32 total, const QString &label)
DeviceInfo(const QString &path, quint64 used, quint64 total, const QString &label)
: path(path), used(used), total(total), label(label)
{
}

QString path = "";
quint32 used = 0;
quint32 total = 0;
quint64 used = 0;
quint64 total = 0;
QString label = QObject::tr("Removable Disk");
QString uuid = "";
QString fstype = "";
Expand Down
129 changes: 105 additions & 24 deletions src/libdbm/util/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

#include "utils.h"

#include <QtCore>

Check warning on line 7 in src/libdbm/util/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QtCore> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QString>

Check warning on line 8 in src/libdbm/util/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QString> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QProcess>

Check warning on line 9 in src/libdbm/util/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QProcess> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <XSys>

Check warning on line 11 in src/libdbm/util/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <XSys> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <dirent.h>

Check warning on line 12 in src/libdbm/util/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <dirent.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTextCodec>
#include <QByteArray>
#include <QRegExp>
Expand Down Expand Up @@ -235,8 +236,15 @@
#ifdef Q_OS_UNIX
QMap<QString, DeviceInfo> CommandDfParse()
{
qDebug() << "Step 2: Execute df command to get disk usage";
QProcess df;
df.start("df", QStringList { "-k", "--output=source,used,avail" });
// 排除网络文件系统类型,避免因为远程挂载卡死
df.start("df", QStringList{"-k", "--output=source,used,avail",
"-x", "nfs",
"-x", "cifs",
"-x", "smbfs",
"-x", "sshfs",
"-x", "ftpfs"});
df.waitForFinished(-1);

QString dfout = df.readAll();
Expand All @@ -258,6 +266,7 @@
qDebug() << "device path" << devInfo.path << "used: " << devInfo.used << "total: " << devInfo.total;
deviceInfos.insert(devInfo.path, devInfo);
}
qDebug() << "Step 2 completed: df command finished, got" << deviceInfos.size() << "disk info";
return deviceInfos;
}

Expand Down Expand Up @@ -290,31 +299,38 @@

QMap<QString, DeviceInfo> CommandLsblkParse()
{
qDebug() << "Step 3: Execute lsblk command to get device details";
QProcess lsblk;
lsblk.start("lsblk", QStringList { "-b", "-p", "-P", "-o", "name,label,size,uuid,fstype,type" });
lsblk.waitForFinished(-1);
qDebug() << "Step 3 completed: lsblk command finished";
QString line;
DeviceInfo info;
QString diskDevPath;
QMap<QString, DeviceInfo> deviceInfos;
// currentPartPath用记录当前的磁盘设备,如sdb
QString currentPartPath = "";
int deviceCount = 0;
do {
bool isPart = false;
line = QString::fromUtf8(lsblk.readLine());
if (line.isEmpty())
break;

QString type;
QRegExp reg("NAME=\"(.*)\" LABEL=\"(.*)\" SIZE=\"(.*)\" UUID=\"(.*)\" FSTYPE=\"(.*)\" TYPE=\"(.*)\"");
QRegExp reg("NAME=\"(.*)\" LABEL=\"(.*)\" SIZE=\"(\\d+)\" UUID=\"(.*)\" FSTYPE=\"(.*)\" TYPE=\"(.*)\"");

if (reg.indexIn(line) >= 0) {
deviceCount++;
info.path = reg.cap(1);
info.label = reg.cap(2);
info.total = reg.cap(3).toLongLong() * 1024 / 1024; // MB
info.uuid = reg.cap(4);
info.fstype = reg.cap(5);
type = reg.cap(6);

qDebug() << "Parse device" << deviceCount << ":" << info.path << "Type:" << type;

if (!type.compare("disk")) {
diskDevPath = info.path;
isPart = false;
Expand Down Expand Up @@ -347,15 +363,18 @@
if (isPart && !diskDevPath.isEmpty() && (info.path.left(currentPartPath.length()) == currentPartPath)) {
info.isDisk = false;
info.strDev = diskDevPath;
qDebug() << " Add partition to device" << diskDevPath << ":" << info.path;
deviceInfos[diskDevPath].children.insert(info.path, info);
} else { // 否则就是 part, 如sdb。
info.isDisk = true;
info.strDev = "";
qDebug() << " Add disk device:" << info.path;
deviceInfos.insert(info.path, info);
// 记录当前是part的情况
currentPartPath = info.path;
}
} while (true);
} while(true);
qDebug() << "Step 2 completed: Parse lsblk output finished, total" << deviceCount << "devices parsed";

return deviceInfos;
}
Expand Down Expand Up @@ -408,6 +427,7 @@
QList<DeviceInfo> ListUsbDrives()
{
qDebug() << "ListUsbDrives";
qDebug() << "Start getting device list";
QList<DeviceInfo> deviceList;
#ifdef Q_OS_WIN32
QFileInfoList extdrivesList = QDir::drives();
Expand All @@ -428,50 +448,102 @@
#ifdef Q_OS_LINUX
QDir devlstdir("/dev/disk/by-id/");
QFileInfoList usbfileinfoL = devlstdir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files);
qDebug() << "Step 1: Scan /dev/disk/by-id/ directory, found" << usbfileinfoL.size() << "device files";

QMap<QString, DeviceInfo> dfDeviceInfos = CommandDfParse();
QMap<QString, DeviceInfo> lsblkDeviceInfos = CommandLsblkParse();

QMap<QString, QString> removeDevice;

for (int i = 0; i < usbfileinfoL.size(); ++i) {
if (usbfileinfoL.at(i).fileName().contains(QRegExp("^usb-\\S{1,}$")) || usbfileinfoL.at(i).fileName().contains(QRegExp("^mmc-\\S{1,}$"))) {
QString path = usbfileinfoL.at(i).canonicalFilePath();
removeDevice.insert(path, usbfileinfoL.at(i).fileName());
QString fileName = usbfileinfoL.at(i).fileName();
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) {
removeDevice.insert(filePath, fileName);
}
}
Comment on lines 467 to 469
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-"));

qDebug() << "Step 4: Filter devices - Found" << removeDevice.size() << "USB/MMC devices from" << usbfileinfoL.size() << "total devices";

for (auto it = removeDevice.begin(); it != removeDevice.end(); ++it) {
qDebug() << " Device path:" << it.key() << "Device name:" << it.value();
}

qDebug() << "Step 5: Traverse device partitions and filter eligible devices";
int processedCount = 0;
int validCount = 0;

for (auto devicePath : lsblkDeviceInfos.keys()) {
processedCount++;
if (!removeDevice.contains(devicePath)) {
continue;
}
qDebug() << "Processing device" << processedCount << ":" << devicePath << "with" << lsblkDeviceInfos.value(devicePath).children.size() << "partitions";
// find first partition
QString strDiskName = devicePath;
DeviceInfo diskinfo = lsblkDeviceInfos.value(devicePath);
QStringList partitionNames = diskinfo.children.keys();

foreach (QString strPartionName, partitionNames) {
bool needformat = true;
DeviceInfo partitionInfo = diskinfo.children.value(strPartionName);
if (partitionNames.isEmpty()) {
// 设备没有分区,作为整个磁盘展示
qDebug() << " Device" << devicePath << "has no partitions, showing as whole disk";
DeviceInfo wholeDevice = diskinfo;
wholeDevice.isDisk = true;
wholeDevice.strDev = "";
wholeDevice.label = wholeDevice.label.isEmpty() ? QObject::tr("Removable disk") : wholeDevice.label;
wholeDevice.needFormat = (wholeDevice.fstype != "vfat");

DeviceInfo dfinfo = dfDeviceInfos.value(devicePath);
if (wholeDevice.total == 0)
wholeDevice.total = dfinfo.total;
wholeDevice.used = dfinfo.used;

deviceList.push_back(wholeDevice);
validCount++;
qDebug() << " Added to device list (item" << validCount << "):" << wholeDevice.path
<< "Label:" << wholeDevice.label
<< "Size:" << wholeDevice.total << "MB"
<< "Need format:" << wholeDevice.needFormat;
} else {
// 设备有分区,遍历分区
foreach (QString strPartionName, partitionNames) {
bool needformat = true;
DeviceInfo partitionInfo = diskinfo.children.value(strPartionName);

if (partitionInfo.fstype != "vfat") {
needformat = true;
} else {
needformat = false;
}
qDebug() << " Check partition" << strPartionName << "filesystem:" << partitionInfo.fstype;

DeviceInfo dfinfo = dfDeviceInfos.value(strPartionName);
if (partitionInfo.label.isEmpty()) {
partitionInfo.label = partitionInfo.path;
}
if (partitionInfo.fstype != "vfat") {
needformat = true;
qDebug() << " Filesystem is not vfat, need format";
}
else {
needformat = false;
qDebug() << " Filesystem is vfat, no need to format";
}

partitionInfo.used = dfinfo.used;
partitionInfo.total = dfinfo.total;
partitionInfo.target = dfinfo.target;
partitionInfo.needFormat = needformat;
deviceList.push_back(partitionInfo);
qDebug() << partitionInfo.path << partitionInfo.used << partitionInfo.total << partitionInfo.target << partitionInfo.needFormat;
DeviceInfo dfinfo = dfDeviceInfos.value(strPartionName);
if (partitionInfo.label.isEmpty()) {
partitionInfo.label = partitionInfo.path;
}

partitionInfo.used = dfinfo.used;
partitionInfo.total = dfinfo.total;
partitionInfo.target = dfinfo.target;
partitionInfo.needFormat = needformat;
deviceList.push_back(partitionInfo);
validCount++;
qDebug() << " Added to device list (item" << validCount << "):" << partitionInfo.path
<< "Size:" << partitionInfo.total << "MB"
<< "Need format:" << needformat;
qDebug() << partitionInfo.path << partitionInfo.used << partitionInfo.total << partitionInfo.target << partitionInfo.needFormat;
}
}
}
qDebug() << "Step 5 completed: Processed" << processedCount << "devices, added" << validCount << "partitions to list";
#endif

#ifdef Q_OS_MAC
Expand All @@ -498,6 +570,15 @@
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
<< "Filesystem:" << deviceList.at(i).fstype
<< "Size:" << deviceList.at(i).total << "MB"
<< "Used:" << deviceList.at(i).used << "MB"
<< "Need format:" << deviceList.at(i).needFormat;
}
return deviceList;
}

Expand Down
Loading
Loading