带O_TRUNC参数的_open调用已同步OneDrive文件返回EINVAL错误求助
OneDrive企业版同步后文件调用
_open带O_TRUNC返回EINVAL问题说明 问题复现
最小复现代码
#include<io.h> int main() { char path[] = "C:\\Users\\<my_username>\\OneDrive - <my_employer>\\Documents\\file.bin"; int fh = _open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, _S_IREAD | _S_IWRITE); if (fh >= 0) { _write(fh, "somebytes", 9); _close(fh); } else { int err = errno; char* message = strerror(err); } return 0; }
复现表现
- 目标文件不存在时,
_open可正常创建文件并写入内容 - 目标文件存在但未同步到OneDrive时,
_open可正常打开、截断内容后写入 - 目标文件存在且已完成OneDrive同步时,
_open调用失败,errno被设置为EINVAL(无效参数)
移除参数中的O_TRUNC标识后函数可正常调用,但无法满足写入前截断原文件的需求,该错误返回不符合CRT官方对_open函数的错误码定义。
环境说明
使用OneDrive 2021 64位版本(内部版本号21.150.0725.0001),仅企业版OneDrive环境复现,个人版OneDrive无此问题。该问题同时会导致存放在OneDrive同步目录下的Visual Studio项目构建失败,表现为构建过程中无法覆盖已同步的项目文件。
问题原因
该问题由企业版OneDrive的同步锁定机制导致:企业版OneDrive基于SharePoint架构实现,相比个人版有更严格的同步文件权限控制,文件完成云同步后,本地缓存的文件会被添加特殊的同步锁和占位符属性。CRT封装的_open函数处理O_TRUNC参数时,底层逻辑和该特殊属性冲突,才会返回不符合文档定义的EINVAL错误。
解决方案
配置层面绕过(无需修改代码)
- 打开OneDrive客户端设置,关闭「文件随选」功能,让同步目录下的所有文件完全保存在本地,不使用占位符模式
- 将需要频繁写入的目录(如Visual Studio项目目录、netcdf/hdf5文件输出目录)加入OneDrive同步排除列表,避开同步锁校验,代价是对应目录的内容不会被同步到云端
代码层面修复
- 调用
_open前先判断文件是否存在,如存在先调用_unlink删除原文件,再执行原_open逻辑,即可实现和O_TRUNC完全一致的效果,规避直接打开已同步文件时的参数冲突 - 替换CRT的
_open函数为原生Win32 APICreateFileW,传入CREATE_ALWAYS参数即可实现等同于O_RDWR | O_CREAT | O_TRUNC的行为,Win32原生API对OneDrive同步文件的兼容性远好于CRT的POSIX兼容层封装,不会触发该错误 - 如使用netcdf/hdf5等第三方库,可在编译时指定库使用Win32原生IO接口而非POSIX兼容层接口,大部分科学计算文件库都提供对应的编译选项,无需修改业务代码即可解决问题
永久修复
升级OneDrive企业版客户端到2022年及之后的发布版本,微软已经在后续更新中修复了该CRT兼容接口的适配问题,升级后无需任何额外配置即可正常调用带O_TRUNC参数的_open函数。
内容的提问来源于stack exchange,提问作者Phil Rosenberg
相关产品推荐
相关产品推荐

