正确判断只读媒体需检查文件系统挂载状态而非文件权限:linux用stat()获取st_dev后解析/proc/mounts,macos用getfsstat()+statfs(),windows用getvolumeinformation()查file_read_only_volume;fallback方案是尝试o_excl|o_creat创建临时文件并严格校验erofs或error_access_denied错误码。

用 stat() 检查文件系统挂载选项是否含 ro
只读媒体(如 CD-ROM、只读挂载的 USB 设备)的本质是整个文件系统被内核以只读方式挂载,而非单个文件的权限位。因此不能靠 access(path, W_OK) 或检查 st_mode 中的写权限位——这些在只读挂载下仍可能返回“可写”,因为权限位属于文件元数据,不反映挂载状态。
正确做法是:先用 stat() 获取文件所在挂载点的设备号(st_dev),再遍历 /proc/mounts(Linux)或调用 getmntent(),匹配设备号并解析挂载选项字段。若选项含 ro(且不含 rw 覆盖),即判定为只读媒体。
-
stat()必须作用于路径所在目录(如stat("/mnt/cdrom/.", &sb)),而非任意子文件,否则可能跨挂载点 - 注意符号链接:
stat()不解引用,lstat()也不适用;应先用realpath()归一化路径,再取其父目录 -
/proc/mounts是 Linux 特有;macOS 需用getfsstat()+statfs()查f_flags & MNT_RDONLY
Windows 下通过 GetVolumeInformation() 判断卷是否只读
Windows 没有挂载选项概念,但每个卷(volume)有独立的只读属性。关键不是路径本身,而是它所属的卷。需提取路径的卷根(如 "D:\foo\bar" → "D:\"),再调用 GetVolumeInformation()。
该函数输出参数 lpFileSystemFlags 若包含 FILE_READ_ONLY_VOLUME 标志,说明该卷物理只读(CD/DVD、写保护的 SD 卡等)或管理员强制设为只读。
- 路径必须是绝对路径;相对路径需先用
GetFullPathName()展开 - 若路径指向网络驱动器(如
Z:),GetVolumeInformation()仍有效,但只读标志取决于远程卷配置 - 注意:U 盘通常不设此标志,即使被硬件写保护——Windows 可能仅在底层 I/O 时才报错,需额外尝试
CreateFile()写入测试
跨平台 fallback:尝试创建临时文件并捕获错误
当系统 API 不可用或挂载信息不可靠(如某些容器环境、FUSE 文件系统),最直接的方式是实操验证:在目标路径同级目录下尝试创建一个临时文件,并检查失败原因是否为只读。
重点不是“能否创建”,而是失败时的错误码:errno == EROFS(Linux/macOS)或 GetLastError() == ERROR_ACCESS_DENIED(Windows)才真正对应只读媒体;EACCES 或 ERROR_PERMISSION_DENIED 可能只是权限不足,不等于媒体只读。
- 务必使用
O_EXCL | O_CREAT(POSIX)或CREATE_NEW(Windows),避免覆盖已有文件干扰判断 - 临时文件应立即
unlink()或DeleteFile(),哪怕创建失败也要清理残留句柄 - 不要依赖
open()返回值为 -1 就断定只读——需严格比对错误码,否则会把磁盘满、配额超限等误判
常见误判场景与绕过限制的现实考量
真实环境中,“只读媒体”边界模糊:有些 USB 设备支持软写保护开关,有些 CD 驱动器允许模拟写入缓存,还有些 FUSE 文件系统(如 archivemount)挂载只读归档却报告可写权限位。此时 API 返回和实际行为可能不一致。
如果你的程序只需“避免写入失败”,比起提前判断,更可靠的做法是:写操作加 try-catch / errno 检查,失败后按只读逻辑降级处理(如提示用户保存到其他位置)。预判只读状态的价值,往往不如优雅处理写入失败来得实在。
尤其要注意:Linux 的 overlayfs 或 bind mount 可能嵌套多层,stat().st_dev 指向下层设备,但挂载选项只对当前层生效——这种情况下,/proc/mounts 匹配必须基于完整路径的挂载点,而非设备号。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











