windows下需通过卷guid路径(如\?olume{...})而非盘符访问efi系统分区,调用createfilew时须加file_flag_backup_semantics标志,且以管理员权限运行;linux下应先用lsblk -f确认fat32挂载点,再以sudo mount -t vfat -o fmask=0133,dmask=0022手动挂载并校验权限。

Windows下用CreateFile打开EFI系统分区需要绕过驱动器号限制
Windows默认不给EFI系统分区(ESP)分配盘符,所以不能直接用fopen("E:\efi\boot\bootx64.efi")这种路径。你得先用GetVolumePathNamesForVolumeNameW或FindFirstVolumeW枚举出ESP对应的卷GUID路径,比如\\?\Volume{a1b2c3d4-...}\,再拼上子路径。
常见错误是硬编码盘符(如E:),或者用GetLogicalDrives()漏掉无盘符卷——ESP几乎总是无盘符的。
- 调用
FindFirstVolumeW获取第一个卷句柄,循环用FindNextVolumeW比对GetVolumeInformationByHandleW返回的lpFileSystemName是否为"FAT32"且dwVolumeSerialNumber与已知ESP匹配(可通过diskpart list volume提前确认) - 拿到卷路径后,必须用
CreateFileW以GENERIC_READ打开,且dwFlagsAndAttributes要含FILE_FLAG_BACKUP_SEMANTICS,否则打开目录失败 - 读取文件时,
ReadFile对二进制EFI镜像没问题,但别用std::ifstream默认模式——它可能触发文本换行转换,破坏PE头
Linux下挂载EFI分区前需确认FAT类型和权限
Linux通常把ESP挂到/boot/efi,但不是绝对的。运行lsblk -f或findmnt -t vfat才能确认实际挂载点。如果没挂载,手动挂载时容易错用ext4选项或漏掉umask=0077导致普通用户读不到。
EFI分区一定是FAT32(少数旧设备用FAT16),但内核模块vfat加载后,挂载参数稍有差异就影响可读性。
- 检查是否已挂载:
mount | grep -i efi,输出类似/dev/sda1 on /boot/efi type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro) - 若未挂载,用
sudo mount -t vfat -o fmask=0133,dmask=0022,uid=1000,gid=1000 /dev/sda1 /mnt/efi(fmask/dmask比umask更精准控制FAT权限) - C++里用
std::ifstream读取时,确保路径是挂载点下的绝对路径,如/boot/efi/EFI/Microsoft/Boot/bootmgfw.efi;不要依赖~展开——std::ifstream不处理波浪线
跨平台读取要注意FAT文件名大小写和长文件名截断
EFI固件只认小写路径(如efi/boot/bootx64.efi),但FAT32在Linux/Windows下默认支持长文件名和混合大小写。C++程序硬写大写路径可能在某些嵌入式UEFI环境失败;而Windows API返回的文件名可能是短名(BOOTX64~1.EFI)。
更隐蔽的问题是:FAT目录项里长文件名字段可能被损坏或不标准,readdir或FindFirstFileW可能跳过某些条目,尤其当ESP被Windows快速启动“休眠”后残留锁文件时。
- 统一用小写字母拼路径,例如
"efi\boot\bootx64.efi"(Windows)或"efi/boot/bootx64.efi"(Linux) - 避免依赖
FindFirstFileW遍历结果排序——FAT目录无序,bootx64.efi和BOOTX64.EFI可能指向同一文件但哈希不同 - 读取前先
stat或GetFileAttributesW确认存在,而不是靠异常或返回空数据判断
权限和安全策略常被忽略
Windows下即使你是管理员,直接打开ESP卷也可能被ERROR_ACCESS_DENIED拦截——这是由于Windows 10+启用了“受保护的进程轻量级”(PPL)或BitLocker加密卷的访问限制。Linux下则可能是SELinux策略或systemd自动卸载了/boot/efi。
最典型的静默失败:程序返回成功,但读出的数据全是零字节,实际是驱动拒绝了I/O请求。
- Windows:以管理员权限运行,且在
CreateFileW中显式指定SECURITY_IMPERSONATION_LEVEL和SECURITY_CONTEXT_TRACKING(极少需要,但调试时加SetLastError检查很关键) - Linux:检查
cat /proc/mounts | grep efi确认挂载选项含rw而非ro;若用systemd-boot,注意bootctl可能锁定ESP - 无论哪边,都别在生产代码里
sleep(1)等挂载完成——应轮询access()或GetFileAttributesExW直到返回有效值
真正麻烦的不是读文件本身,而是确认“此刻这个路径确实对应正在使用的EFI系统分区”,尤其在多系统、双硬盘、BitLocker+TPM启用的机器上——卷GUID可能变,挂载点可能漂移,固件更新后分区结构还可能重排。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











