diff --git a/debian/changelog b/debian/changelog index 39627855..1fe8bb1b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +deepin-boot-maker (5.7.12) unstable; urgency=medium + + * Support creating bootable USB drives from unpartitioned USB disks + + -- XuShitong Fri, 07 Nov 2025 16:11:17 +0800 + deepin-boot-maker (5.7.11) unstable; urgency=medium * a permission promote issue was resolved. diff --git a/src/libdbm/installer/qtbaseinstaller.cpp b/src/libdbm/installer/qtbaseinstaller.cpp index ed874395..bc9ea0bd 100644 --- a/src/libdbm/installer/qtbaseinstaller.cpp +++ b/src/libdbm/installer/qtbaseinstaller.cpp @@ -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; + } + } + + // 如果是 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; } } diff --git a/src/libdbm/util/deviceinfo.h b/src/libdbm/util/deviceinfo.h index 020e68c4..88c9ac37 100644 --- a/src/libdbm/util/deviceinfo.h +++ b/src/libdbm/util/deviceinfo.h @@ -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 = ""; diff --git a/src/libdbm/util/utils.cpp b/src/libdbm/util/utils.cpp index d92ae027..a79ec2c6 100644 --- a/src/libdbm/util/utils.cpp +++ b/src/libdbm/util/utils.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -235,8 +236,15 @@ void ClearTargetDev(const QString &targetPath) #ifdef Q_OS_UNIX QMap 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(); @@ -258,6 +266,7 @@ QMap CommandDfParse() 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; } @@ -290,15 +299,18 @@ static QByteArray unescapeLimited(const QString &str) QMap 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 deviceInfos; // currentPartPath用记录当前的磁盘设备,如sdb QString currentPartPath = ""; + int deviceCount = 0; do { bool isPart = false; line = QString::fromUtf8(lsblk.readLine()); @@ -306,15 +318,19 @@ QMap CommandLsblkParse() 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; @@ -347,15 +363,18 @@ QMap CommandLsblkParse() 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; } @@ -408,6 +427,7 @@ bool isUsbDisk(const QString &dev) QList ListUsbDrives() { qDebug() << "ListUsbDrives"; + qDebug() << "Start getting device list"; QList deviceList; #ifdef Q_OS_WIN32 QFileInfoList extdrivesList = QDir::drives(); @@ -428,50 +448,102 @@ QList ListUsbDrives() #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 dfDeviceInfos = CommandDfParse(); QMap lsblkDeviceInfos = CommandLsblkParse(); QMap 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); } } + 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 @@ -498,6 +570,15 @@ QList ListUsbDrives() 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; } diff --git a/src/vendor/src/libxsys/DiskUtil/DiskUtil.cpp b/src/vendor/src/libxsys/DiskUtil/DiskUtil.cpp index 94b319b7..dfbf916c 100644 --- a/src/vendor/src/libxsys/DiskUtil/DiskUtil.cpp +++ b/src/vendor/src/libxsys/DiskUtil/DiskUtil.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #ifdef Q_OS_WIN32 #include @@ -328,7 +330,8 @@ const QString MountPoint(const QString &targetDev) udev /dev tmpfs /run */ - XSys::Result ret = XSys::SynExec("df", "--output=source,target"); + // 排除网络文件系统类型,避免因为远程挂载导致 df 卡死 + XSys::Result ret = XSys::SynExec("df", "--output=source,target -x cifs -x nfs -x smbfs -x sshfs -x ftpfs -x davfs"); if (!ret.isSuccess()) { qWarning() << "call df failed" << ret.result(); @@ -624,6 +627,54 @@ bool FormatPartion(const QString& targetDev) return XSys::SynExec("mkfs.fat", targetDev).isSuccess(); } +bool CreatePartition(const QString& diskDev) +{ +#ifdef Q_OS_LINUX + // 使用fdisk创建主分区(全盘创建一个主分区) + // o: 创建DOS分区表 + // n: 创建新分区 + // p: 主分区 + // 1: 分区号1 + // 默认起始扇区 + // 默认结束扇区(使用全部空间) + // w: 写入磁盘 + + qDebug() << "Creating partition for disk" << diskDev; + + QString cmd = QString("echo -e 'o\\nn\\np\\n1\\n\\n\\nw' | fdisk ") + diskDev; + XSys::Result result = XSys::SynExec("bash", "-c \"" + cmd + "\""); + + if (!result.isSuccess()) { + qWarning() << "Failed to create partition table with fdisk for" << diskDev; + return false; + } + qDebug() << "Partition table created, reloading kernel partition table..."; + + // 通知内核重新读取分区表 + XSys::SynExec("partprobe", diskDev); + + // 等待分区创建完成 + QThread::sleep(1); + + // 检查分区是否创建成功 + std::string partPath = diskDev.toStdString() + "1"; + struct stat buffer; + bool success = (stat(partPath.c_str(), &buffer) == 0); + + if (success) { + qDebug() << "Partition" << QString::fromStdString(partPath) << "created successfully"; + } else { + qWarning() << "Partition creation verification failed for" << QString::fromStdString(partPath); + } + + return success; +#else + // Windows或其他系统暂不支持自动创建分区 + (void)diskDev; + return false; +#endif +} + QStringList GetPartionOfDisk(const QString& strDisk) { QStringList strPartions; @@ -702,7 +753,7 @@ void SetPartionLabel(const QString& strPartion, const QString& strImage) QString strName = QString("UNKNOWN"); QString strTemp; - if (2 == strValues.size()) { + if (2 <= strValues.size()) { strTemp = strValues.at(1); strTemp = strTemp.trimmed(); } @@ -812,7 +863,7 @@ bool UmountPartion(const QString& strPartionName) bool UmountDisk(const QString &targetDev) { - bool bRet = false; + bool bRet = true; int iIndex = targetDev.lastIndexOf("/"); if (iIndex < 0) { @@ -827,6 +878,7 @@ bool UmountDisk(const QString &targetDev) } QStringList strPartions = GetPartionOfDisk(strKey); + strPartions.append(strKey); foreach (QString strPartion, strPartions) { QString strVal = strPath + strPartion; diff --git a/src/vendor/src/libxsys/DiskUtil/DiskUtil.h b/src/vendor/src/libxsys/DiskUtil/DiskUtil.h index 7d0c9f16..0b289bd0 100644 --- a/src/vendor/src/libxsys/DiskUtil/DiskUtil.h +++ b/src/vendor/src/libxsys/DiskUtil/DiskUtil.h @@ -31,6 +31,7 @@ qint64 GetPartitionFreeSpace(const QString &targetDev); qint64 GetPartitionTotalSpace(const QString &targetDev); QString GetPartitionLabel(const QString &targetDev); bool FormatPartion(const QString& targetDev); +bool CreatePartition(const QString& diskDev); QStringList GetPartionOfDisk(const QString& strDisk); bool SetActivePartion(const QString& strDisk, const QString& strPartion); void SetPartionLabel(const QString& strPartion, const QString& strImage);