前言

当哈希校验不通过时,适用本软件恢复。

本篇为数据管理第三步
文件筛选整理 -> 生成哈希文件 -> 生成冗余数据 -> 定时同步多处备份

第二步请参考这篇文章:哈希校验及监测

  1. 冗余恢复软件简介
    假如生成了5%的冗余数据,当任意部分数据丢失不超过5%时,可以恢复丢失的部分。称得上是保护个人数据的神器

    冗余数据的大小与设置的块大小及冗余百分率有关。当设置了5%的恢复率及块大小约4096时(-s4096参数),冗余数据占用的空间约原文件的5%左右。

  1. 本方案适用范围
    适用于需要长时间存储的数据。除了备份一份外,加上冗余数据是最省成本及最安全的做法,因为占用空间小。
  1. 冗余软件选择
    Parchive 系列(par2格式) 或 winrar的“添加 winrar 恢复记录“ 。使用 Parchive 系列软件生成单独存在的恢复记录,恢复块与存档放在一起,可以校验及恢复记录。

    最方便灵活就是Parchive 系列(par2格式) 。都是用了 里德-所罗门码(Reed–Solomon error correction)实现冗余。该算法已经纳入了磁盘阵列 RAID 5/6 中。

小结
记录的数据要同时写下一份SHA256blake3等哈希检验信息。方便知道那些数据已经失效。点击该检验信息自动校验。

有加密的数据要多留备份及加“恢复数据”,因为加密的数据稍有损坏,就会打不开,解密不回来。

添加冗余数据不一定可以恢复(损坏多于冗余率时),如果有两个备份,损坏位置不一样,可以用 par2 软件结合第 2 个文件(备份文件)来恢复。

定时同步多处备份可以使用FreeFileSync, rsync,Syncthing,BorgBackup,rclone等软件同步,多种介质异地备份。放到云端的数据可用cryptomator等软件自动透明加密。遵守备份的3-2-1 原则即可。不开篇细说。

Parchive 客户端总结

多平台命令行用: par2cmdline

只有命令行,全平台。在终端执行命令 par2 。其它平台界面基本上都是基于这个命令行核心的。多文件操作时适用。

Windows 平台

Windows 下直接用MultiPar 即可,这是最完善的 par2 客户端,图形界面与命令行都有,还自带命令行批处理脚本示例。BUG 修复快,细节满分,教程详细,多国语言。备用par2cmdline。—-建议去GitHub为这两个项目打star支持,最好有捐赠支持开发。

MultiPar 的安装目录(或解压目录)下,这个文件有对应的说明,很详细的教程,包括参数设置,0804 文件夹对应是中文的。包含生成 par2 的批处理示例,设置好批处理里面的 par2j.exe 的路径即可用:
./Help/0804/index.Htm
/Help/ 目录下有命令行的参数说明。

:不管用那个平台,都可以参考 MultiPar 的帮助文件入门 par2

Linux 平台

Linux 下主用:par2cmdline
Linux下 GUI 界面用: Easy Par2PyPar2

Debian 及 ubuntu 可以用以下命令安装。其它 Linux 发行版可以搜关键字 par2cmdlinepar2 来查看相关包名。

apt install par2

MacOS 平台

macOS 下主用:par2cmdline
macOS 下 GUI 界面用: MacPAR deLuxe

安装命令:

brew install par2

备用客户端

推荐使用上面的par2客户端,以下为备用客户端。可能随着时间情况会有变化。

一、 Par2deep
版本 V1.9.4 的路径处理有 BUG,例如带英文中括号的路径就不能识别。版本 V1.9.5 系列有其它 BUG,无法正常用。

Par2deep 全平台,有图形界面及命令行,用 Python 及 PyQT5 写的可以递归操作。安装后在终端执行命令 par2deepnohup par2deep & 命令行可以用 par2deep-cli

可以用 pyinstaller 结合 pipreqs 确认依赖,然后打包成一个文件:

# 进入项目目录后
pipreqs ./ --encoding=utf-8 --force
# 会得到 requirements.Txt 文件,导入依赖到环境
python3 -m pip install -r requirements. txt 
# 然后用 PyInstaller 打包成单独一个可以执行的文件。
python3 -m PyInstaller x1/x2/xxx.py --clean --onefile 

其它 python打包类似可用 Nuitka

二、ParPar
只能创建 PAR2 文件,不能验证及修复。但高性能。

三、gopar
用 go 语言写的。

生成数据冗余文件

少量文件,直接用对应平台的图形界面客户端即可,不用怎么管理。

以下示例均是基于 par2cmdline 的命令行,LinuxmacOS 通用。批量处理文件适用。
Windows 可以用 MultiPar 自带的批处理示例,本教程不重复造轮子。

方式一:单个或少量文件生成

用各平台对应的图形界面客户端软件即可,设置好冗余数据比例。
生成,验证,修复均可在界面直接点击。

方式二:本目录及子目录内所有文件批量生成

特点:生成的所有 par2 文件在执行的目录,不可以针对单独一个文件校验修复。验证或修复的时候会同时验证其余正常的文件,会较费不必要的时间。灵活性低。后期增减单个文件麻烦。适合不移动数据目录及不添加修改的文件夹,最适合什么都不改动的存档文件(例如非常适合光盘内容)。所有 par2 文件都在一个目录内,方便管理 par2 文件。

cd 进入该目录,运行
生成:

# -R 递归到子目录中生成。
par2 create -n1 -r5 -s2048 -R *

验证:

par2 verify *.par2

修复:

par2 repair *.par2

方式三:有选择性批量生成
灵活性高。可选排除适合在硬盘上生成的文件类型。Par2 文件存放在对应数据文件的目录下的隐藏文件夹下(示例的文件夹以 .hide_par2dir 命名)。

块大小:-s,数值越大,产生的冗余文件越小,下面表示2048字节块大小即2KB块大小。冗余率:-r,可损坏的百分比。冗余文件数量:-n,写 1 代表共生成 2 个 par2 文件,如此类推。

递归批量生成命令:添加 -n1 为只生成两个 par2 文件,-r5 为 5%的冗余数据率。生成的par2文件与源文件在同一个目录下。

find . -maxdepth 6 ! -path "*/.hide_par2dir/*" -type f ! -name "._*" -type f ! -name "sha256.sum" -type f ! -name "blake3.sum" -type f ! -name ".DS_Store" -type f  -print | xargs -I {} par2 create -n1 -r5 -s2048  ./{}.par2 ./{}
# 验证
par2 verify abc.txt.par2 abc.txt 

# 修复
par2 repair abc.txt.par2 abc.txt 

移动生成的冗余文件

本节适用于上述的第三种方法产生的 par2 文件。不是用第三种方法的可跳过本节。

修改移动的脚本,加入生成的命令,当该目录所有文件都生成par2文件时,移动到隐藏文件夹。或者一步到位,直接生成par2文件在隐藏文件夹里。

批量移动 par2 校验文件到 par2 的隐藏目录。在每个含 par2 文件的目录下生成 . Hide_par2dir 隐藏文件夹,并移动 par2 文件到该目录。

只处理当前目录

把以下命令存入一个 .sh 文件,添加执行权限:

#!/bin/bash
files=$(ls ./*.par2 2> /dev/null | wc -l)

if [ "$files" -ne 0 ] ;then 
    if [ ! -d "./.hide_par2dir" ]; then mkdir .hide_par2dir ;fi;
    mv ./*.par2 ./.hide_par2dir/ ;
fi

注意:上述命令只处理当前目录下的par2文件。

递归处理子目录

以下为上面命令的递归子目录版本,移动所有 par2 文件到该目录下的新建的隐藏目录.hide_par2dir

#!bin/bash
## 脚本功能: 批量移动par2校验文件到par2的隐藏目录. 
## 在每个含par2文件的目录下生成 .hide_par2dir 隐藏文件夹, 并移动par2文件到该目录.

# dir=$(pwd)  # 当前工作目录,不一定是脚本目录
# dir='/data/'  # 某固定目录
dir="$(cd `dirname $0`; pwd)"  # 脚本文件所在目录
# dir 为要处理的初始目录.
hidedir='.hide_par2dir'  # 要隐藏的par2目录名

function move_file_to_par2dir(){
    cd "$1"
    temp_ls_data=$( ls "$1" );

    oldIFS=$IFS  # 更改分隔符为换行符,不然for循环会按空格分隔.
    IFS=$'\n'

    mvtime=0 ;  # 初始移动par2文件次数
    for result in ${temp_ls_data}
    do
        dir_or_file="$1"'/'${result}

        if [ -d "${dir_or_file}" ];then  # 是目录的话

            # 当不是par2隐藏目录,递归遍历.
            if [[ "${dir_or_file}" != "$1"'/'"${hidedir}" ]]; 
                then move_file_to_par2dir "${dir_or_file}" ; 
            fi  

        else   # 如果是文件的话
            if [ ${mvtime} -eq 0 ] ;then
                files_sum=$(echo "${temp_ls_data}" | grep '\.par2$' | wc -l)
                # 如果当前目录含有par2文件
                if [ "${files_sum}" -ne 0 ] ;then 
                    if [ ! -d "./${hidedir}" ]; then mkdir "./${hidedir}" ;fi;

                    mv ./*.par2 ./${hidedir}/ ;
                    mvtime=1 ;  # 当前目录下所有par2文件已经移动过
                fi
            fi
        fi
    done

    IFS=$oldIFS  # 还原初始分隔符
}

move_file_to_par2dir "${dir}"

隐藏冗余文件目录

如果不使用 Windows 可以跳过本节
上述步骤以点开头的文件夹,已经实现了在 Linux 和 macOS 下隐藏文件夹的目的,以下为 windows 下隐藏该文件夹的方法。

移动生成的所有 *.par2 文件到一个固定格式名称的隐藏目录下,平时不放在一起,用到时候再复制(移动)出来修复。文件夹名称加点号,Windows 下 cmd 设置隐藏属性 attrib "文件名" +s +h参考教程 。(提高)有脚本检测删除了什么文件,以便清理 par2 文件。

Windows 系统隐藏文件夹批处理:

@echo off
title hide par2 folder
for /r /d %%i in ( .hide_par2dir ) do ( if exist "%%i" attrib +s +h "%%i" )
rem pause

Windows 系统显示文件夹批处理:

@echo off
title show par2 folder
for /r /d %%i in ( .hide_par2dir ) do ( if exist "%%i" attrib -s -h "%%i" )
rem pause

说明:上述批处理保存为 .cmd 后缀或.bat文件。只有第 3 行 for 循环那行是必须的。如果要在窗口输入,把 %%i 换成 %i 。Rem 为注释,设置 @echo on 及取消注释 pause 可以显示实际处理信息。可以把.hide_par2dir 换成想要隐藏的文件夹名。Windows 下不能界面生成点开头的文件夹名,只能命令生成。

修复故障文件

实现:把哈希异常的文件自动运行 par2 命令修复

步骤:

  1. 发生故障时记录“故障的目录“,记录到固定路径的文件中(例如 home 下文件名 err_par2dir.txt)。脚本读取err_par2dir.txt文件里的故障目录列表后第一个 for 循环,cd 进入到该故障目录,读取该目录的故障文件列表,第二个 for 循环。一般没有那么多文件故障,循环只是逻辑需要。
  2. 每一行就是一个文件,从文件提取故障的“文件名“。
  3. 运行 par2 相关命令修复,指定故障文件修复文件 ,路径要拼接好,对应的目录拼接为转移后的目录。
    其余命令参考par2 -h 的说明。

MacOS 系统,自动修复文件脚本:

#!bin/bash
## 脚本功能: 读取错误目录列表及相应损坏文件列表,自动运行par2修复错误命令。
## 如果成功修复,删除备份的损坏文件。删除错误目录及文件列表。

# dir=$(pwd)  # 当前工作目录,不一定是脚本目录
# dir='/data/'  # 某固定目录
# dir="$(cd `dirname $0`; pwd)"  # 脚本所在目录
hidedir='.hide_par2dir'  # 要隐藏的par2文件的目录名
def_err_filename='sum_fail.txt'  # 默认在故障目录存放故障文件名的txt文件名
def_err_dirname='/var/tmp/err_par2dir.txt'  # 存放在固定路径的, 故障对应目录的文件名. 不要写 ~ 等变量,不同身份运行会变的.

# 如果文件不存在就退出
if [ ! -f "${def_err_dirname}" ] ;then exit ;fi ;

# 去除重复行
## sort -u "${def_err_dirname}" | tee "${def_err_dirname}"
## awk '!a[$0]++' "${def_err_dirname}" | tee "${def_err_dirname}"
# 加载故障目录数据
## errdata_dir_names=$( cat "${def_err_dirname}" ) ;
errdata_dir_names=$( sort -u "${def_err_dirname}" | tee "${def_err_dirname}" )

if [ -n "${errdata_dir_names}" ] ;then  # 如果有故障目录的数据
    oldIFS=$IFS  # 更改分隔符为换行符,不然for循环会按空格分隔.
    IFS=$'\n'

    # 循环目录
    for temp_dirname in ${errdata_dir_names}
    do
        cd ${temp_dirname} ;  # 进入该故障目录

        errdata_filenames=$( cat "${temp_dirname}"'/'"${def_err_filename}" ) ;

        # 循环目录下的文件名
        for tempfilename in ${errdata_filenames}
        do
            # echo '-----内for循环开始------'

            # 先移动par2文件(实际是改名)出来,再恢复错误的文件.恢复完后再移动回去
            mv "$(pwd)"'/'"${hidedir}"'/'"${tempfilename#*./}"'.par2' "$(pwd)"'/'
            mv "$(pwd)"'/'"${hidedir}"'/'"${tempfilename#*./}"'.vol'*'.par2' "$(pwd)"'/'

            # 必须同一个目录才能修复. 原生自带的par2命令加 -p 参数,当修复成功时,删除备份的损坏文件同时删除par2备份文件. 是直接删除而不是移动到回收站.
            repair_status=$( par2 repair "${temp_dirname}"'/'"${tempfilename#*./}"'.par2' "${temp_dirname}""${tempfilename:1}" | grep 'Repair complete.' | wc -l )

            # 监听判断是否成功, 读取输出记录,看有没有成功关键字.成功就输出到特定文件记录
            if [ $(eval echo ${repair_status}) -gt 0 ] && [ -f "${temp_dirname}"'/'"${tempfilename#*./}"'.1' ] ; then
                # 删除备份的损坏文件
                rm "${temp_dirname}"'/'"${tempfilename#*./}"'.1'
                # 用 sed 删除文件列表里的该文件名所在的行
                # macos 上要加上 -e 参数,sed -i '' -e ,Linux上 sed -i 就可 .
                sed -i '' -e '/^\.\/'"${tempfilename:2}"'$/d' "${temp_dirname}"'/'"${def_err_filename}"
              fi

            # 移动par2备份文件回原来位置
            mv "${temp_dirname}"'/'"${tempfilename#*./}"'.par2' "${temp_dirname}"'/'"${hidedir}"'/'
            mv "${temp_dirname}"'/'"${tempfilename#*./}"'.vol'*'.par2' "${temp_dirname}"'/'"${hidedir}"'/'

            # echo '-----内for循环结束------'
        done
        # 最里面的循环结束后删除该错误文件
        # 当文件内容为空时候, 才删除.
        if [ ! -s "${temp_dirname}"'/'"${def_err_filename}" ] ; then
            rm "${temp_dirname}"'/'"${def_err_filename}" ;
            #echo "---def_err_filename内容为空,删除---"
        fi

        # 删除单项故障目录, macos 上要加上 -e 参数,sed -i '' -e ,Linux上 sed -i 就可 .        
        temp_rm_linenumber=$(grep -wn "${temp_change_sedstr}" "${def_err_dirname}" | cut -d : -f 1)
        sed -i '' -e "${temp_rm_linenumber}"'d' "${def_err_dirname}"
    done


    if [ ! -s "${def_err_dirname}" ] ; then
        rm "${def_err_dirname}" ;
        #echo "---def_err_dirname内容为空,删除---"
    fi

    IFS=$oldIFS  # 还原初始分隔符
fi

Linux 系统,修复文件脚本,其余内容与macOS的脚本相同:

# 大约51及69行,把 sed -i '' -e 换成 sed -i 即可:
sed -i '/^\.\/'"${tempfilename:2}"'$/d' "${temp_dirname}"'/'"${def_err_filename}"

Windows系统,参考 MultiPar 程序自带的批处理示例及帮助教程。也可直接界面点击操作修复。