Linux挂载手机时std::filesystem::copy失效但原生拷贝正常的原因
问题分析与解答
问题现象
在Linux本地环境运行正常的多文件移动/复制代码,通过USB连接手机并挂载存储后出现以下异常:
- 使用
std::filesystem::copy向手机存储复制文件时,仅创建0字节的空文件 - 使用
std::filesystem::rename移动文件时,出现文件丢失的情况 - 原生
std::ifstream+std::ofstream实现的拷贝函数可正常工作,std::filesystem::copy_file则报错“Operation not supported”
核心原因
USB挂载的手机存储并非标准POSIX文件系统
手机通过USB文件传输模式挂载的存储,大多基于**MTP(媒体传输协议)**或类似的伪文件系统,这类系统不支持POSIX标准的系统级文件操作:std::filesystem底层依赖sendfile、copy_file_range、rename等高效系统调用,但MTP对这些调用做了不兼容处理或直接不支持std::filesystem::copy尝试调用系统级拷贝失败后,仍执行了创建空文件的步骤,最终生成0字节文件;std::filesystem::rename在跨文件系统时本应执行“拷贝+删除源文件”的逻辑,但MTP环境下拷贝失败后源文件已被删除,导致文件丢失
std::filesystem与原生流的实现差异std::ifstream和std::ofstream基于用户态的read/write基础系统调用,MTP文件系统对这类通用读写操作做了兼容适配,因此能正常完成文件传输std::filesystem::copy_file直接调用专用的系统级拷贝接口,而MTP不支持该接口,因此返回“Operation not supported”错误
跨文件系统移动的逻辑缺陷
当源文件在本地POSIX文件系统、目标在MTP挂载点时,std::filesystem::rename无法执行原子移动(仅同文件系统支持原子rename),会自动转为“拷贝到目标+删除源文件”的流程。但MTP环境下拷贝步骤失败后,源文件的删除操作仍可能执行,最终导致文件丢失。
解决方案建议
- 针对MTP这类非POSIX文件系统,强制使用原生流的拷贝方式替代
std::filesystem的copy/copy_file函数 - 实现移动操作时,先通过流拷贝文件到目标路径,确认拷贝成功后再手动删除源文件,避免中途出错导致文件丢失
- 可提前通过
std::filesystem::status或系统命令判断目标路径的文件系统类型,根据类型选择对应的操作逻辑
用户提供的代码
void actual_copy_or_move( const std::filesystem::path& current_path, const std::filesystem::path& target_path) { std::error_code err_code {}; if (m_do_move) { // Moving the file. std::filesystem::rename(current_path, target_path, err_code); } else { // Straightforward copy, without any extra checks. auto copy_lambda {[¤t_path, &target_path]() { std::ifstream input_stream {current_path, std::ios::binary}; std::ofstream output_stream {target_path, std::ios::binary}; if (!input_stream.good() || !output_stream.good()) { std::wcerr << L"Couldn't open files for copying: " << current_path.wstring() << " => " << target_path.wstring() << "\n"; return; } output_stream << input_stream.rdbuf(); }}; // Copying the file. #if 0 //std::filesystem::copy_file(current_path, target_path, err_code); std::filesystem::copy(current_path, target_path, std::filesystem::copy_options::update_existing, err_code); #else copy_lambda(); #endif } if (err_code) { std::wcerr << L"what(): " << StringHelper::string_to_wstring(err_code.message()) << "\n"; } }
内容的提问来源于stack exchange,提问作者Tigrolik
相关产品推荐
相关产品推荐

