不能直接用os.Stat+ModTime判断同步,因NTFS精度仅100ms、NFS/FAT32时间戳截断或不一致,且os.ReadDir.Info()与os.Stat()结果可能冲突;应统一用os.Stat获取完整元数据,并结合Size、Mode及inode/FileIndex做强校验。

为什么 os.Stat + ModTime 不能直接用于增量判断
ModTime 在 NFS、Docker volume 挂载点、Git 工作区或 FAT32/NTFS 文件系统上常被截断、延迟甚至批量重写。比如 touch 一个文件,内容没变但 ModTime 更新了,就会误判为“变更”;反过来,编辑器快速保存可能因时间精度丢失(NTFS 仅 100ms)导致 ModTime 不变,漏同步。更糟的是,os.ReadDir 返回的 fs.DirEntry.Info() 和单独调用 os.Stat() 可能返回不同时间戳——因为前者不保证加载完整元数据。
必须统一用 os.Stat() 获取完整信息,并至少结合 Size() 和 Mode() 判断;对关键文件,还得加内容校验。
如何安全高效地做内容比对(小文件 vs 大文件)
全量计算 sha256.Sum256 对每个文件都来一遍,IO 和 CPU 开销太大,尤其面对 GB 级视频或日志。实际应分层处理:
- 先比
Size():不等直接跳过,90% 的差异靠这一步就筛掉了 - 小文件(hash.Hash +
io.Copy分块读取,控制内存在几 MB 内 - 大文件:只读前 64KB 和后 64KB,算两个
sha256.Sum256;不一致再 fallback 全量校验 - 永远别用
io.ReadAll加载整个文件进内存——它会把 2GB 文件全塞进 RAM
注意:哈希必须用 sha256.Sum256,不是 md5;后者碰撞风险高,且 Go 标准库对 SHA256 的流式支持更稳。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
filepath.WalkDir 遍历时怎么防崩、防越界、防权限卡死
filepath.WalkDir 是 Go 1.16+ 推荐方式,但它默认不处理符号链接循环、也不自动忽略 Access is denied 错误,一碰就 panic。
正确做法是:
- 遍历前先
filepath.Clean()输入路径,防止../绕过根目录限制 - 遇到
syscall.ENOENT或os.ErrNotExist,直接return nil继续,别 panic - 想跳过
.git、node_modules:在回调里检查entry.Type().IsDir() && strings.HasPrefix(entry.Name(), "."),然后return fs.SkipDir - 跨平台过滤用
path.Match("*.tmp", entry.Name()),别用strings.HasSuffix—— Windows 路径分隔符是\,容易匹配失败
tar 打包时路径净化和元数据保留的关键细节
archive/tar 不做路径净化,Header.Name 原样保留源路径。如果文件路径含 ../ 或绝对路径,解压时可能覆盖系统关键位置——这不是 bug,是设计使然。
必须手动净化:
- 写入
tar.Writer前,对每个路径调用filepath.Clean(),再检查是否以..开头或包含..路径段 - 更稳妥:用
filepath.Rel(root, absPath)计算相对于备份根目录的相对路径,确保Header.Name干净 - 恢复时,不要直接拼接路径,而是先
filepath.Join(restoreRoot, header.Name),再用strings.HasPrefix检查结果是否仍在restoreRoot内部 - 权限和时间戳要显式设置:
os.Chmod(f, header.FileInfo().Mode())和os.Chtimes(path, header.AccessTime, header.ModTime);注意AccessTime和ModTime在 Go 1.19+ 才被tar.Reader正确解析
真正难的不是哈希或打包,而是路径 normalize、错误分类收敛、跨平台行为统一——Windows 的大小写不敏感、长路径限制、权限模型,会让看似简单的同步在某个 Access is denied 上突然卡住,而错误日志里只显示 “permission denied”,根本看不出是符号链接未解引用还是管理员权限缺失。

















