概述
- 磁盘擦除相关
相关概念
ata
- 是计算机存储设备(如
HDD/SSD
)的接口协议标准,定义了设备与主机间的通信方式,包括数据传输、设备控制和安全命令集
- 是计算机存储设备(如
Sanitize
- 是
ATA
规范中专门用于安全擦除数据的一组命令,属于ATA
指令集的扩展功能(如ATA Sanitize Device
命令) - 它通过发送特定指令触发设备内部的物理级擦除操作
- 是
擦除方法
- 对于
SSD
Nvme
:可以使用nvme format
- 非
Nvme
:可以使用Ata sanitize
- 对于
HDD
- 可以使用
diskpart
系统工具(Windows
平台)
- 可以使用
diskpart
clean all
- 概述
- 操作对象是整块物理磁盘(
Physical Disk
),而非单个分区(Partition
) - 该命令会删除磁盘上所有分区表信息(包括
MBR/GPT
结构),并将磁盘恢复为“未初始化”状态
- 操作对象是整块物理磁盘(
ssd
- 会对整个磁盘(不是分区)进行全零覆盖
- 虽尝试覆写,但因磨损均衡机制无法覆盖全部物理区块
hdd
- 会全盘覆写
0
值(每个扇区填充0x00
)
- 会全盘覆写
delete partition
- 删除当前选中的分区(需先用
select partition
指定焦点) - 操作层级:仅仅删除分区表相关信息,并不会擦除整个分区数据
delete partition override
override
参数- 强制绕过系统保护机制,允许删除受保护分区(如恢复分区、
OEM
分区、EFI
系统分区等)
- 强制绕过系统保护机制,允许删除受保护分区(如恢复分区、
- 操作层级:仅删除分区表信息(
MBR/GPT
中的条目),不擦除实际数据
format
概述
先0
后随机
- 一次随机覆盖
- 覆盖逻辑固定为“先全零,后随机数”
1 |
format e: /fs:NTFS /p:1 |
- 实践发现
- 需要输入卷标
- 需要输入
Y
确认格式化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
C:\Windows\System32>format e: /fs:NTFS /p:1 文件系统的类型是 NTFS。 输入驱动器 E: 的当前卷标新加卷 警告,非移动磁盘驱动器 E: 上的所有数据将会丢失! 是否继续进行格式化(Y/N)?y 正在格式化 12.5 GB 卷标(32 个字符;如果没有,请按 Enter)? 正在创建文件系统结构。 格式化已完成。 总共有 12.5 GB 的磁盘空间。 12.5 GB 可用。 C:\Windows\System32> |
cipher
概述
Windows
系统内置的命令行工具,主要用于NTFS
文件系统的加密管理(EFS
)和安全擦除操作
加解密文件
- 对指定文件或目录启用
EFS
(加密文件系统)加密,后续新增文件自动继承加密属性- 加密父目录可避免文件修改时意外解密
1 2 |
cipher /E C:\SecretDocs # 加密文件夹 cipher /E /A C:\File.txt # 加密单个文件 |
1 |
cipher /D C:\SecretDocs # 解密文件夹 |
1 2 3 |
// 对目录及其所有子目录和文件批量加密/解密 cipher /E /S:C:\Projects # 加密整个目录树 |
- 加密后能否访问文件/文件夹?
- 当前登录用户可以正常访问,无需额外操作
EFS
加密是透明的:用户登录后,系统自动用其私钥解密文件,访问过程无感知 - 其他用户或未授权账户:无法访问
尝试打开文件时会提示“拒绝访问”,因为系统无法用其他用户的私钥解密文件 - 特殊情况:
若加密文件被复制到FAT/FAT32
分区,会自动解密;移动到NTFS
分区 则保持加密状态
- 当前登录用户可以正常访问,无需额外操作
- 解密是否需要密码?
- 默认情况下无需密码:
EFS
解密依赖 用户登录凭证(非独立密码)。只要用户登录系统,即可自动解密文件 - 需密码的场景:
密钥恢复:若用户重装系统或账户被删除,需导入备份的密钥文件(.PFX
)。导入时需输入 导出时设置的密码(若有)
多用户共享:通过cipher /ADDUSER
添加其他用户时,需目标用户的 证书或密钥(非密码),但操作需管理员权限
- 默认情况下无需密码:
安全擦除功能
- 覆盖未使用磁盘空间,彻底清除已删除文件的残留数据(支持全零覆盖)
- 原理
HDD
:三次覆写(0x00
→0xFF
→ 随机值),符合基础安全标准SSD
:通过TRIM
通知主控标记无效数据,避免物理覆写损耗
1 |
cipher /W:C:\Temp # 清理C盘Temp目录所在卷的空闲空间 |
- 注意
- 仅处理未分配空间,不影响现有文件
SSD
推荐优先使用此命令而非物理覆写(如format /P
),减少写入放大
Nvme format
概述
- 用于对
NVMe
固态硬盘进行底层格式化的管理命令,涉及逻辑块格式调整、安全擦除和命名空间配置 NVMe Format
命令通常针对整个物理磁盘或命名空间(Namespace
),无法仅擦除单个分区(如E
盘)
Ata sanitize
概述
- 是一种针对存储设备(尤其是固态硬盘
SSD
)的底层数据销毁技术,通过发送特定命令触发控制器对介质的物理级清理,确保数据不可恢复 ATA Sanitize
命令是针对整个物理磁盘的操作,而不是针对单个分区(如E
盘)
物理级擦除
ATA Sanitize
直接作用于存储介质的物理块(Block
),而非仅操作逻辑映射表。它通过以下方式实现数据销毁:Block Erase
:对NAND
闪存的每个存储单元施加高电压擦除信号,彻底清除电子记录(恢复至出厂状态)Crypto Erase
:删除加密密钥(如设备支持硬件加密),使加密数据永久失效(无需覆写物理介质)Overwrite
(不推荐):用固定模式(如全零)覆写数据,但因增加SSD
写入损耗而少用
协议支持范围
- 非
NVMe
固态(SATA/SAS
)SATA SSD
Block Erase
:物理擦除NAND
块(适用于所有SATA SSD
)
Crypto Erase
:删除加密密钥(需硬件加密支持)
Overwrite
:覆写数据(不推荐用于SSD
)SAS SSD
通过SCSI
命令集实现Sanitize
,功能与SATA
版本类似
NVMe
固态NVMe SSD
(需协议版本≥1.3
)
NVMe 1.3
标准新增Sanitize
命令,功能与ATA
版本对齐
示例代码
- 检查是否支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool disk_ata_help::check_sanitize_support(LPCWSTR driver_path) { std::wstring devicePath = L"\\\\.\\" + std::wstring(1, driver_path[0]) + L":"; HANDLE hDevice = CreateFileW(devicePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { return false; } std::vector<BYTE> identifyData(512, 0); if (!send_ata_command(hDevice, 0xEC, identifyData)) { CloseHandle(hDevice); return false; } CloseHandle(hDevice); return true; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
bool disk_ata_help::send_ata_command(HANDLE hDevice, BYTE command, std::vector<BYTE>& data, BYTE feature /*= 0*/) { SCSI_PASS_THROUGH_DIRECT apt = {0}; apt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); apt.PathId = 0; apt.TargetId = 0; apt.Lun = 0; apt.CdbLength = 16; apt.SenseInfoLength = 0; apt.DataIn = data.empty() ? SCSI_IOCTL_DATA_UNSPECIFIED : SCSI_IOCTL_DATA_IN; apt.DataTransferLength = static_cast<ULONG>(data.size()); apt.TimeOutValue = 60; apt.DataBuffer = data.empty() ? nullptr : data.data(); apt.SenseInfoOffset = 0; //apt.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX); //apt.DataTransferLength = static_cast<ULONG>(data.size()); apt.Cdb[0] = 0x85; // ATA PASSTHROUGH操作码 apt.Cdb[1] = 0x08; // PROTOCOL=4 (PIO Data-In) apt.Cdb[2] = 0x0E; // BYTE_BLOCK=1, T_LENGTH=2, T_DIR=1 apt.Cdb[3] = 0x00; // FEATURES (15:8) apt.Cdb[4] = feature; // FEATURES (7:0) apt.Cdb[5] = 0x00; // SECTOR_COUNT (15:8) apt.Cdb[6] = 0x01; // SECTOR_COUNT (7:0) - 至少1个扇区 apt.Cdb[7] = 0x00; // LBA_LOW (15:8) apt.Cdb[8] = 0x00; // LBA_LOW (7:0) apt.Cdb[9] = 0x00; // LBA_MID (15:8) apt.Cdb[10] = 0x00; // LBA_MID (7:0) apt.Cdb[11] = 0x00; // LBA_HIGH (15:8) apt.Cdb[12] = 0x00; // LBA_HIGH (7:0) apt.Cdb[13] = command; // ATA命令 apt.Cdb[14] = 0xA0; // DEVICE HEAD (LBA模式) apt.Cdb[15] = 0x00; const ULONG inputSize = sizeof(SCSI_PASS_THROUGH_DIRECT); std::vector<BYTE> inputBuffer(inputSize); memcpy(inputBuffer.data(), &apt, inputSize); const ULONG outputSize = sizeof(SCSI_PASS_THROUGH_DIRECT) + (apt.SenseInfoLength > 0 ? apt.SenseInfoLength : 0); std::vector<BYTE> outputBuffer(outputSize); /*void* dataBuffer = data.empty() ? nullptr : data.data(); const ULONG bufferSize = sizeof(SCSI_PASS_THROUGH_DIRECT) + (data.empty() ? 0 : data.size()); std::vector<BYTE> buffer(bufferSize); memcpy(buffer.data(), &apt, sizeof(apt));*/ /*apt.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED; std::vector<BYTE> buffer(sizeof(ATA_PASS_THROUGH_EX) + data.size()); memcpy(buffer.data(), &apt, sizeof(apt));*/ DWORD bytesReturned = 0; if (!DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, inputBuffer.data(), static_cast<DWORD>(inputSize), outputBuffer.data(), static_cast<DWORD>(outputSize), &bytesReturned, NULL)) { DWORD err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); return false; } /*DWORD bytesReturned = 0; if (!DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH, buffer.data(), static_cast<DWORD>(buffer.size()), buffer.data(), static_cast<DWORD>(buffer.size()), &bytesReturned, NULL)) { auto err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); return false; }*/ if (!data.empty() && apt.DataIn == SCSI_IOCTL_DATA_IN) { SCSI_PASS_THROUGH_DIRECT* pOutput = reinterpret_cast<SCSI_PASS_THROUGH_DIRECT*>(outputBuffer.data()); BYTE* dataStart = reinterpret_cast<BYTE*>(pOutput) + sizeof(SCSI_PASS_THROUGH_DIRECT); memcpy(data.data(), dataStart, data.size()); } return true; } |
- 命令
1 2 3 4 5 6 7 8 9 10 11 12 |
std::wstring devicePath = L"\\\\.\\" + std::wstring(1, path[0]) + L":"; HANDLE hDevice = CreateFileW(devicePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { return false; } auto res = disk_ata_help::send_sanitize_command(hDevice, SanitizeType::BLOCK_ERASE); return res; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bool disk_ata_help::send_sanitize_command(HANDLE hDevice, SanitizeType type) { BYTE feature = 0; switch (type) { case BLOCK_ERASE: feature = 0x01; break; case CRYPTO_ERASE: feature = 0x02; break; case OVERWRITE: feature = 0x03; break; default: return false; } std::vector<BYTE> tmp; return send_ata_command(hDevice, 0xB4, tmp, feature); } |
trim
概述
TRIM
本质上是操作系统与SSD
之间的一种通信机制,用于优化垃圾回收(GC
)过程- 它通过特定指令通知
SSD
哪些数据块已被删除,从而避免无效数据的搬运
细节
- 操作系统通知
SSD
:“某些逻辑块地址(LBA
)的数据已无效,无需保留” SSD
主控据此标记物理块为Invalid
,后续 GC 直接跳过这些块SSD
的FTL
(闪存转换层)更新LBA→PBA
映射表,标记对应物理块为可回收状态
TRIM
不立即擦除数据,而是标记无效状态,由SSD
后台异步执行物理擦除
核心作用
- 操作系统与
SSD
的通信机制- 当用户删除文件时,操作系统通过
TRIM
命令(ATA
接口)或等效命令(SCSI
的UNMAP
、NVMe
的Deallocate
)通知SSD
哪些逻辑块地址(LBA
)的数据已无效 SSD
据此标记物理块为“Invalid
”,后续GC
可直接跳过这些块,避免无效数据搬运
- 当用户删除文件时,操作系统通过
- 总结1:
- TRIM标记的
LBA
(逻辑块地址)会被SSD
主控标记为“Invalid
”,垃圾回收(GC
)过程会跳过这些块,不会搬运其中的数据
- TRIM标记的
指令实现(按接口协议分类)
ATA/SATA
接口- 指令名称:
TRIM
(DataSet Management Command
) - 命令码:
0x06
- 指令名称:
1 2 3 4 5 6 |
struct TrimCommand { uint8_t command; // 0x06 uint8_t feature; // 0x01 (TRIM标志) uint64_t lba; // 起始逻辑块地址 uint16_t count; // 连续块数量 }; |
NVMe
接口- 指令名称:
Deallocate
(或Dataset Management
) - 命令码:
0x09
- 指令名称:
1 2 3 4 5 6 |
struct DeallocateCommand { uint32_t namespace_id; // 命名空间ID uint64_t slba; // 起始LBA uint32_t nlb; // 块数量 uint8_t attributes; // 属性(如时效性) }; |
SCSI/SAS
接口- 指令名称:
UNMAP
- 命令码:
0x42
- 指令名称:
1 2 3 4 5 6 7 8 |
struct UnmapCommand { uint16_t group; // 组号(通常为0) uint32_t block_count; // 待取消映射的块数 struct { uint64_t lba; // LBA地址 uint32_t count; // 连续块数 } ranges[]; // LBA范围数组 }; |
关于被标记的块
TRIM
标记的块是否允许直接写入新数据?SSD
主控需在后台擦除标记为Invalid
的物理块(NAND
闪存特性要求写入前必须先擦除)- 新数据写入时,
SSD
的FTL
(闪存转换层)会建立新的LBA→PBA
(物理块地址)映射,覆盖旧映射
分区擦除简述
概述
- 分区的擦除,和普通文件的不一样,要对分区盘符进行权限独占
开始时独占权限
1 |
bool res = volume_help::acquire_exclusive_access(hFile); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
bool volume_help::acquire_exclusive_access(HANDLE hVolume) { if (!lock_volume(hVolume)) { DWORD err = GetLastError(); return false; } if (!dismount_volume(hVolume)) { DWORD err = GetLastError(); DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, NULL, NULL); return false; } /*if (!prevent_remount(hVolume)) { DWORD err = GetLastError(); DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, NULL, NULL); return false; }*/ //close_open_handles(driveLetter); DWORD bytesReturned = 0; if (!DeviceIoControl(hVolume, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &bytesReturned, NULL)) { DWORD err = GetLastError(); DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL); return false; } return true; } |
1 2 3 4 5 6 7 8 9 10 11 |
bool volume_help::lock_volume(HANDLE hVolume) { DWORD bytesReturned = 0; return DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL); } bool volume_help::dismount_volume(HANDLE hVolume) { DWORD bytesReturned = 0; return DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL); } |
结束时恢复
1 2 3 4 5 6 7 8 |
void volume_help::release_exclusive_access(HANDLE hVolume) { DWORD bytesReturned = 0; DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL); DeviceIoControl(hVolume, FSCTL_MARK_VOLUME_DIRTY, NULL, 0, NULL, 0, &bytesReturned, NULL); } |
实现
- 示例实现,大于
50gb
开始分线程,最大4
个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
bool disk_help::secure_erase_logic_driveletter_full_partition_new_sync( const wchar_t* driveLetter) { std::wstring path = L"\\\\.\\" + std::wstring(driveLetter).substr(0, 2); HANDLE hFile = CreateFileW( path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); if (hFile == INVALID_HANDLE_VALUE) { return false; } bool res = volume_help::acquire_exclusive_access(hFile); ULONGLONG diskSize = 0; DWORD sectorSize = 512; if (!get_disk_geometry(hFile, diskSize, sectorSize)) { CloseHandle(hFile); return false; } DWORD chunkSize = getOptimalChunkSize(hFile); chunkSize = (chunkSize + sectorSize - 1) / sectorSize * sectorSize; GET_LENGTH_INFORMATION lengthInfo = { 0 }; DWORD bytesReturned = 0; if (!DeviceIoControl(hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &lengthInfo, sizeof(lengthInfo), &bytesReturned, NULL)) { CloseHandle(hFile); return false; } ULONGLONG partitionSize = lengthInfo.Length.QuadPart; ULONGLONG numThreads = partitionSize / (50 * one_gb); if (partitionSize % bytes_per_therad != 0) { ++numThreads; } numThreads = min(numThreads, ULONG(4)); std::vector<std::thread> workers; LONGLONG blockSize = partitionSize / numThreads; LONGLONG currentOffset = 0; for (int i = 0; i < numThreads; ++i) { ULONGLONG start = currentOffset; ULONGLONG end = (i == numThreads - 1) ? partitionSize : start + blockSize; currentOffset = end; start = (start / sectorSize) * sectorSize; end = (end / sectorSize) * sectorSize; workers.emplace_back([hFile, start, end, chunkSize, sectorSize]() { std::unique_ptr<BYTE, void (*)(void*)> buffer( static_cast<BYTE*>(_aligned_malloc(chunkSize, chunkSize)), _aligned_free); ULONGLONG offset = start; bool success = true; while (offset < end && success) { ULONGLONG remaining = end - offset; DWORD toWrite = static_cast<DWORD>( min(static_cast<ULONGLONG>(chunkSize), remaining)); toWrite = (toWrite / sectorSize) * sectorSize; if (toWrite == 0) { success = false; break; } LARGE_INTEGER moveDistance; moveDistance.QuadPart = offset; if (!SetFilePointerEx(hFile, moveDistance, NULL, FILE_BEGIN)) { auto err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); std::string str_msg = str_help::wstr_to_str(msg.c_str()); if (logger) { logger->info("cur offset is : {}", offset); logger->info("cur offset SetFilePointerEx failed error_code is : {}", err); logger->info("cur offset SetFilePointerEx failed error msg is : {}", str_msg); } //success = false; //break; } else { if (logger) { logger->info( "cur offset SetFilePointerEx <success>..."); } } /*int maxRetries = 3; for (int attempt = 0; attempt < maxRetries; attempt++) { DWORD bytesWritten = 0; if (WriteFile(hFile, buffer, 2*toWrite, &bytesWritten, NULL)) { break; } DWORD err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); if (err == ERROR_WRITE_FAULT || err == ERROR_CRC) { std::this_thread::sleep_for( std::chrono::milliseconds(100 * (attempt + 1))); } else { break; } }*/ DWORD bytesWritten = 0; if (WriteFile(hFile, buffer.get(), toWrite, &bytesWritten, NULL)) { if (logger) { logger->info("success : cur offset is : {}", offset); } /*break*/; } else { DWORD err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); auto str_msg = str_help::wstr_to_str(msg.c_str()); if (logger) { logger->info("cur offset is : {}", offset); logger->info("cur offset WriteFile failed error_code is : {}", err); logger->info("cur offset WriteFile failed error msg is : {}", str_msg); } /*if (err == ERROR_WRITE_FAULT || err == ERROR_CRC) { } else { break; }*/ } logger->flush(); offset += toWrite; //progress.update(bytesWritten); } }); } for (auto& t : workers) { t.join(); } FlushFileBuffers(hFile); volume_help::release_exclusive_access(hFile); CloseHandle(hFile); return true; } |
擦除方案
SSD
nvme ssd
diskpart
不行,恢复可能高nvme format
针对的是整个物理磁盘Ata sanitize
针对的是整个物理磁盘
- 关于分区擦除的其他方式
- 多线程写入擦除,详细见分区擦除简述
HDD
format
- 其他方式待记
未使用空间
cipher
问题记录
概述
- 上述方法,擦除处理普通分区是没问题的
- 但是,如果要擦除的分区有进程正在运行,就不行了,直接在最开始锁定卷的时候会失败,报权限问题
- 或者,如果要擦除的分区有页面文件卷等受保护内容,也不行,直接在最开始锁定卷的时候会失败,报权限问题
关于有进程运行的情况
- 问题描述
- 比如
E
盘里面现在运行了一个进程,这时对E盘进行锁定,会失败,报Access is denied.
- 比如
- 解决方案
关于有受保护内容的情况
- 问题描述
- 比如有
pagefile.sys
等内容
- 比如有
- 解决方案
其他记录
wstr
转str
1 2 3 4 5 6 7 8 9 10 11 12 13 |
std::string str_help::wstr_to_str(LPCWSTR wstr) { int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr); if (len == 0) { return ""; } std::string str(len, 0); WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &str[0], len, nullptr, nullptr); str.resize(len - 1); return str; } |
error_code
转str
1 2 3 4 5 6 7 8 9 10 11 12 |
std::wstring str_help::get_win32_error_string(DWORD errorCode) { LPWSTR buffer = nullptr; DWORD size = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&buffer), 0, NULL); std::wstring result(buffer, size); LocalFree(buffer); return result; } |
获取簇或块大小
Windows
中,簇与块是等价概念
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ULONGLONG disk_help::get_volume_blocksize(LPCWSTR filePath) { wchar_t volumePath[MAX_PATH] = {0}; if (!GetVolumePathNameW(filePath, volumePath, MAX_PATH)) { return 4096; } DWORD sectorsPerCluster = 0, bytesPerSector = 0; if (!GetDiskFreeSpaceW(volumePath, §orsPerCluster, &bytesPerSector, NULL, NULL)) { return 4096; } return static_cast<ULONGLONG>(sectorsPerCluster) * bytesPerSector; } |
获取分区的物理磁盘编号
- 获取分区的物理磁盘编号,判断下两个分区是否在同一个物理磁盘上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
std::set<DWORD> disk_help::get_physical_disk_numbers(const wchar_t* driveLetter) { std::set<DWORD> diskNumbers; std::wstring volumePath = L"\\\\.\\" + std::wstring(driveLetter).substr(0, 2); HANDLE hVolume = CreateFileW(volumePath.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hVolume == INVALID_HANDLE_VALUE) { return diskNumbers; } VOLUME_DISK_EXTENTS volumeExtents; DWORD bytesReturned = 0; if (DeviceIoControl(hVolume, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &volumeExtents, sizeof(volumeExtents), &bytesReturned, NULL)) { for (DWORD i = 0; i < volumeExtents.NumberOfDiskExtents; i++) { diskNumbers.insert(volumeExtents.Extents[i].DiskNumber); } } else { STORAGE_DEVICE_NUMBER deviceNumber; if (DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &deviceNumber, sizeof(deviceNumber), &bytesReturned, NULL)) { diskNumbers.insert(deviceNumber.DeviceNumber); } } CloseHandle(hVolume); return diskNumbers; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
bool disk_help::is_on_same_physical_disk(const wchar_t* drive1, const wchar_t* drive2) { auto disks1 = disk_help::get_physical_disk_numbers(drive1); auto disks2 = disk_help::get_physical_disk_numbers(drive2); for (auto& item : disks1) { if (logger) { logger->info("disk_help : is_on_same_physical_disk : goal_path numbers_item is {}", item); } } for (auto& item : disks2) { if (logger) { logger->info( "disk_help : is_on_same_physical_disk : sys_path numbers_item is {}", item); } } logger->flush(); for (DWORD disk : disks1) { if (disks2.find(disk) != disks2.end()) { return true; } } return false; } |
是否固态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
bool disk_help::is_ssd(LPCWSTR path) { wchar_t drivePath[4] = {0}; if (path[1] == L':') { drivePath[0] = path[0]; drivePath[1] = L':'; drivePath[2] = L'\\'; } else { return false; } std::wstring devicePath = L"\\\\.\\" + std::wstring(1, drivePath[0]) + L":"; HANDLE hDrive = CreateFileW(devicePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDrive == INVALID_HANDLE_VALUE) { auto err = GetLastError(); return false; } STORAGE_PROPERTY_QUERY query{}; query.PropertyId = StorageDeviceSeekPenaltyProperty; query.QueryType = PropertyStandardQuery; DEVICE_SEEK_PENALTY_DESCRIPTOR descriptor{}; DWORD bytesReturned = 0; BOOL success = DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &descriptor, sizeof(descriptor), &bytesReturned, NULL); CloseHandle(hDrive); return success && bytesReturned >= sizeof(descriptor) && !descriptor.IncursSeekPenalty; } |
是否开启了trim
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bool disk_help::is_enable_trim_reg() { HKEY hKey; DWORD dwValue = 1; DWORD dwSize = sizeof(DWORD); if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { if (RegQueryValueExW(hKey, L"DisableDeleteNotify", NULL, NULL, reinterpret_cast<LPBYTE>(&dwValue), &dwSize) != ERROR_SUCCESS) { dwValue = 0; } RegCloseKey(hKey); } return (dwValue == 0); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
bool disk_help::is_enable_trim_volume(LPCWSTR drivePath) { wchar_t devicePath[MAX_PATH]; if (!QueryDosDeviceW(drivePath, devicePath, MAX_PATH)) { return false; } HANDLE hDevice = CreateFileW(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice == INVALID_HANDLE_VALUE) { return false; } DEVICE_TRIM_DESCRIPTOR trimDesc = {0}; STORAGE_PROPERTY_QUERY query = {}; query.PropertyId = StorageDeviceTrimProperty; query.QueryType = PropertyStandardQuery; DWORD bytesReturned = 0; BOOL result = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &trimDesc, sizeof(trimDesc), &bytesReturned, NULL); CloseHandle(hDevice); return (result && trimDesc.Version >= sizeof(trimDesc) && trimDesc.Size >= sizeof(trimDesc) && trimDesc.TrimEnabled); } |
发ata
命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
bool disk_ata_help::send_ata_command(HANDLE hDevice, BYTE command, std::vector<BYTE>& data, BYTE feature /*= 0*/) { SCSI_PASS_THROUGH_DIRECT apt = {0}; apt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); apt.PathId = 0; apt.TargetId = 0; apt.Lun = 0; apt.CdbLength = 16; apt.SenseInfoLength = 0; apt.DataIn = data.empty() ? SCSI_IOCTL_DATA_UNSPECIFIED : SCSI_IOCTL_DATA_IN; apt.DataTransferLength = static_cast<ULONG>(data.size()); apt.TimeOutValue = 60; apt.DataBuffer = data.empty() ? nullptr : data.data(); apt.SenseInfoOffset = 0; //apt.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX); //apt.DataTransferLength = static_cast<ULONG>(data.size()); apt.Cdb[0] = 0x85; // ATA PASSTHROUGH操作码 apt.Cdb[1] = 0x08; // PROTOCOL=4 (PIO Data-In) apt.Cdb[2] = 0x0E; // BYTE_BLOCK=1, T_LENGTH=2, T_DIR=1 apt.Cdb[3] = 0x00; // FEATURES (15:8) apt.Cdb[4] = feature; // FEATURES (7:0) apt.Cdb[5] = 0x00; // SECTOR_COUNT (15:8) apt.Cdb[6] = 0x01; // SECTOR_COUNT (7:0) - 至少1个扇区 apt.Cdb[7] = 0x00; // LBA_LOW (15:8) apt.Cdb[8] = 0x00; // LBA_LOW (7:0) apt.Cdb[9] = 0x00; // LBA_MID (15:8) apt.Cdb[10] = 0x00; // LBA_MID (7:0) apt.Cdb[11] = 0x00; // LBA_HIGH (15:8) apt.Cdb[12] = 0x00; // LBA_HIGH (7:0) apt.Cdb[13] = command; // ATA命令 apt.Cdb[14] = 0xA0; // DEVICE HEAD (LBA模式) apt.Cdb[15] = 0x00; const ULONG inputSize = sizeof(SCSI_PASS_THROUGH_DIRECT); std::vector<BYTE> inputBuffer(inputSize); memcpy(inputBuffer.data(), &apt, inputSize); const ULONG outputSize = sizeof(SCSI_PASS_THROUGH_DIRECT) + (apt.SenseInfoLength > 0 ? apt.SenseInfoLength : 0); std::vector<BYTE> outputBuffer(outputSize); DWORD bytesReturned = 0; if (!DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, inputBuffer.data(), static_cast<DWORD>(inputSize), outputBuffer.data(), static_cast<DWORD>(outputSize), &bytesReturned, NULL)) { DWORD err = GetLastError(); std::wstring msg = str_help::get_win32_error_string(err); return false; } if (!data.empty() && apt.DataIn == SCSI_IOCTL_DATA_IN) { SCSI_PASS_THROUGH_DIRECT* pOutput = reinterpret_cast<SCSI_PASS_THROUGH_DIRECT*>(outputBuffer.data()); BYTE* dataStart = reinterpret_cast<BYTE*>(pOutput) + sizeof(SCSI_PASS_THROUGH_DIRECT); memcpy(data.data(), dataStart, data.size()); } return true; } |
发sanitize
命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bool disk_ata_help::send_sanitize_command(HANDLE hDevice, SanitizeType type) { BYTE feature = 0; switch (type) { case BLOCK_ERASE: feature = 0x01; break; case CRYPTO_ERASE: feature = 0x02; break; case OVERWRITE: feature = 0x03; break; default: return false; } std::vector<BYTE> tmp; return send_ata_command(hDevice, 0xB4, tmp, feature); } |
检查是否支持sanitize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool disk_ata_help::check_sanitize_support(LPCWSTR driver_path) { std::wstring devicePath = L"\\\\.\\" + std::wstring(1, driver_path[0]) + L":"; HANDLE hDevice = CreateFileW(devicePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { return false; } std::vector<BYTE> identifyData(512, 0); if (!send_ata_command(hDevice, 0xEC, identifyData)) { CloseHandle(hDevice); return false; } CloseHandle(hDevice); return true; } |
是否系统卷
1 2 3 4 5 6 7 |
bool volume_help::is_system_volume(const wchar_t* driveLetter) { wchar_t systemDir[MAX_PATH] = {0}; if (GetSystemDirectoryW(systemDir, MAX_PATH)) { return driveLetter[0] == systemDir[0]; } return false; } |
是否启动卷
1 2 3 4 5 6 7 |
bool volume_help::is_boot_volume(const wchar_t* driveLetter) { wchar_t windowsDir[MAX_PATH] = {0}; if (GetWindowsDirectoryW(windowsDir, MAX_PATH)) { return driveLetter[0] == windowsDir[0]; } return false; } |
简单的线程数量计算
1 2 3 4 5 6 7 |
ULONGLONG partitionSize = lengthInfo.Length.QuadPart; ULONGLONG numThreads = partitionSize / (50 * one_gb); if (partitionSize % bytes_per_therad != 0) { ++numThreads; } numThreads = min(numThreads, ULONG(4)); |
简单的进度条计算
- 每次按
1%
通知,可以控制在99
,最后来一个成功的通知
1 2 3 4 5 6 7 8 9 |
float tmp_progress = float(counted) / end; uint32_t p = static_cast<uint32_t>(tmp_progress * 100); p = min(p, 99u); if (p > progress) { progress = static_cast<float>(p); if (erase_cb) { erase_cb("erase_file", "hello"); } } |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windows高级调试_调试器03/19
- ♥ 线程和协程10/31
- ♥ C++程序高级调试与优化_第一篇07/20
- ♥ Windows动态库(DLL)详细学习:一05/30
- ♥ 打包_7z生成自解压打包exe07/11
- ♥ Windows 核心编程 _ 内核对象二06/07