Bsdiff,Bspatch 的差分增量升级(基于Win和Linux)

这篇具有很好参考价值的文章主要介绍了Bsdiff,Bspatch 的差分增量升级(基于Win和Linux)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

         背景

内容

准备工作

在windows平台上

在linux平台上

正式工作

生成差分文件思路

作用差分文件思路

在保持相同目录结构进行差分增量升级

服务端(生成差分文件)

客户端(作用差分文件)

12月6日更新(修复bug)

执行分析

注意事项和关注的地方:


背景

像常见的Android 的linux平台,游戏,系统更新都会用到这一种方式。

以我自己的理解,这种方式有些像git中的版本管理, 以最少的时间进行版本管理.核心在于如何去记录文件的差异.

服务器端:

通过

 bsdiff  old  new  patchfile_path 

生成差分文件.一般以.patch的文件命名.

客户端: 根据patch文件 通过

bspatch oldfile newfile patchfile_path

一般情况下,本以为可以直接通过压缩包的形式去进行, 安卓平台的.apk文件是可以的,单片机的可执行hex等格式的文件也是可以的. 但通过压缩的压缩包则可能会有隐患. 通过开会讨论以及本人查询资料发现 会因为压缩算法,压缩文件顺序的不一样而导致差分包出现问题.

原因有

主要原因有:

1. 不同的压缩算法会产生不同的压缩数据。即使原始数据相同,通过不同算法压缩结果也不完全一样。这会直接影响bsdiff的比较结果。

2. 即使使用同一压缩算法,压缩文件内原始数据的顺序改变也可能改变压缩效果。压缩算法利用重复模式来达到压缩效果。顺序改变会打乱这种模式。

3. bsdiff是按顺序比较数据生成差分的。所以就算压缩原理数据相同,其在压缩文件中的顺序变化也会导致bsdiff生成不同的差分补丁。

4. 压缩算法本身就利用了字典及顺序来提高压缩率。这与bsdiff的工作原理有一定冲突。综上,为了生成一致的bsdiff补丁,同一个数据生成压缩包时需要保证使用同一算法和稳定的顺序。否则差分结果可能会有较大变化。一般需要压缩数据再差分时,需要注意控制这两个因素,或者考虑在解压后对原始数据文件差分。

所以,考虑解压后保持相同的目录结构进行差分,即为生成的.patch文件和原工程有相同的目录.

所以需要写一个脚本,生成一个差分文件夹(目录),这个差分文件夹与原工程有相同的目录结构.

后面再根据这个差分文件夹进行升级,即为patch文件与原文件作用生成新文件,新目标和原目标相同.通过这种服务器上生成差分包,客户端上作用差分包的形式,差分包可以压缩,在客户端上解压缩,这样能更快更合理.

所以,总共需要有2个bash脚本,一个放服务器上,生成差分包.一个放客户端上,在收到差分包后进行本地升级.

内容

bsdiff和bspatch去官网上截至2023年10月27日没有下载源码的权限,所以得去别的地方找找源码.

准备工作

在windows平台上

参考

whistle713/bsdiff-win: bsdiff Windows binaries and Visual Studio 2015/2019 project. (github.com)

里面有提供能够在windows平台上允许的.exe可执行文件.

在linux平台上

参考

红橙Darren视频笔记 bsdiff bspatch 使用(Linux下)_洌冰的博客-CSDN博客

完成编译

正式工作

这里需要考虑到旧的目标和新的目标的一些特殊情况.

  1. 新目标有新增文件的情况
  2. 新目标有删除原来旧文件的情况
  3. 新目标和旧目标的目录和文件都能对上,只是有变化.
  4. 旧目标和新目标有 大小为0 bytes 文件的情况(bsdiff失效)

相信一般的升级都会遇到 1.2.3.4所有情况,

对于第4种情况,不清楚是不是bsdiff的版本问题还是linux系统的问题,我在本地的liunx没有这个问题.

bsdiff在处理 大小为 0 bytes的文件时在linux上报错

报错

bsdiff:mmap()  xxx:Invalid argument

思路:

对于第一种和第二种情况.

新目标新增: 在旧目标中生成一个相同名字的文件,不过大小为0 bytes

新目标有删除有原来旧文件的情况: 在新目标中生成一个相同名字的文件,大小依然为0bytes

这样的话,只要不出现 4 的这种问题都是能够通过bsdiff 生成相应的bspatch文件的.

生成差分文件思路

1.同步旧目标(对应新目标有文件增加时)

2.同步新目标(对应新目标删除了旧文件时)

3.递归遍历目标中的每一个文件,在另一个目标中进行查找, 可以直接通过bsdiiff 生成差分文件,

即使是两个相同的文件,也会生成patch文件,只不过bspatch 作用这个patch文件时并不会起作用,这样是非常方便了,都不需要进行判断了。这样表现为每一个文件都有对应的差分文件.(这个需要再我的代码上改一改)

而我下面并没有这么做,而是根据md5的值判断文件不同后再生成对应的patch文件.

作用差分文件思路

直接遍历生成的差分文件目录结构,调用bspatch.

在保持相同目录结构进行差分增量升级

服务端(生成差分文件)

调用.

 ./gen.sh(脚本名) ./old(旧目录) ./new (新目录)

最终会生成一个以日期后缀的差分文件的目录(和原目录保持相同的目录结构) 

#!/bin/bash

# check if two arguments are given
if [ $# -ne 2 ]; then
  echo "Usage: $0 oldfolder newfolder"
  exit 1
fi

# check if the arguments are valid directories
if [ ! -d "$1" ] || [ ! -d "$2" ]; then
  echo "Invalid directories"
  exit 2
fi

# if it is ended by '/' delete '/'
inputemp=$1
if test "${inputemp: -1}" == "/";then
    s1=${inputemp%/}
else 
    s1=$inputemp
fi

inputemp=$2
if test "${inputemp: -1}" == "/";then
    s2=${inputemp%/}
else 
    s2=$inputemp
fi

 


# create a new directory for patch files
patch_dir="patch_$(date +%Y%m%d%H%M%S)"
mkdir -p "$patch_dir"

# sync in new target
find "$s1" -type f | while read oldfile; do
  # get the relative path of the file
  rel_path=${oldfile#$s1/}
  # get the corresponding file in the second directory
  newfile="$s2/$rel_path"
  # exist in old and not exist in new and create same name to instead in the new folder 
  if [ ! -f "$newfile" ]; then
    echo -e "\033[0;36m [disapper in new]: $newfile Generate 0 Bytes to instead in new target \033[0m"
     mkdir -p "$(dirname $newfile)"
     > $newfile  
  fi

done


# sync in old target
find "$s2" -type f | while read newfile; do
  # get the relative path of the file
  rel_path=${newfile#$s2/}
  # get the corresponding file in the second directory
  oldfile="$s1/$rel_path"
  # exist in new and not exist in old and create same name to instead in the old folder 
  if [ ! -f "$oldfile" ]; then
  echo -e "\033[0;36m [disapper in old]: $oldfile Generate 0 Bytes to instead in old target  \033[0m"
  #  create the parent directory if needed
    mkdir -p "$(dirname oldfile)"
    > $oldfile  
  fi

done


# Generate patch 
find "$s1" -type f | while read oldfile; do

  # get the relative path of the file
  rel_path=${oldfile#$s1/}
  # get the corresponding file in the second directory
  newfile="$s2/$rel_path"
  # Haved sync and create the patch file name
  patch_file="$patch_dir/$rel_path.patch"
  # create the parent directory if needed
  mkdir -p "$(dirname "$patch_file")"
  # use bsdiff to generate the patch file
  oldmd5=$(md5sum $oldfile | awk '{print $1}')
  newmd5=$(md5sum $newfile | awk '{print $1}')

  if [ "$oldmd5" = "$newmd5" ]; then
      
  echo -e "\033[0;32m Don't Need to Change \033[0m"

  else
      bsdiff "$oldfile" "$newfile" "$patch_file"
      echo -e "\033[0;33mGenerated patch for $rel_path \033[0m"
  fi
   

done

echo "Done. Patch files are in $patch_dir"

客户端(作用差分文件)

 调用

脚本名 旧目标 新目标(也可以是旧目标 ,相当与替换旧目标) 差分目录
#!/bin/bash


# check if two arguments are given
if [ $# -ne 3 ]; then
  echo "Usage: cmd  oldfolder newfolder patchfolders"
  exit 1
fi

# new generate 
if [ ! -e "$2" ]; then
mkdir $2
fi

# check if the arguments are valid directories
if [ ! -d "$1" ] || [ ! -d "$3" ] ; then
  echo "Invalid directories"
  exit 2
fi

# if it is ended by '/' delete '/'
inputemp=$1
if test "${inputemp: -1}" == "/";then
    s1=${inputemp%/}
else 
    s1=$inputemp
fi

inputemp=$2
if test "${inputemp: -1}" == "/";then
    s2=${inputemp%/}
else 
    s2=$inputemp
fi


inputemp=$3
if test "${inputemp: -1}" == "/";then
    s3=${inputemp%/}
else 
    s3=$inputemp
fi






#loop item in path_item
find "$s3" -type f -name "*.patch" | while read patch_item; do
    temp=${patch_item#$s3/}
    temp=${temp%.patch}  #equal to   temp=${temp:0:${#temp}-6}
    oldfile="$s1/$temp"
    newfile="$s2/$temp"
    mkdir -p "$(dirname "$newfile")"
    echo -e "\033[0;32m Generate $oldfile $newfile \033[0m"
    
# execute bspatch
    bspatch "$oldfile" "$newfile" "$patch_item"
  

 

done

如何使用?

一般调用过程

diff -rq ./old ./new(此时会看到文件差异)

./gen ./old ./new

./upgrate ./old ./old ./patch_xxx 

diff -rq ./old ./new (没有输出表示更新升级完毕)

12月6日更新(修复bug)

出现bug,找不到软链接文件,差分升级无法作用在存在的软连接文件.

于是更新.

  •  解决找不到软连接文件的问题,现在能找到所有文件了(包括软链接和隐藏文件)
  •  删除同步过程中的空目录和空文件(0字节文件)

 Bsdiff,Bspatch 的差分增量升级(基于Win和Linux),Linux,linux,服务器

gen.sh 

#!/bin/bash


#record waiting_for_delete_index
wddi="waiting_for_dindex"
GetAllfile()
{
        local ret=()
        local links=$(find "$1" -type l)
        local normal_files=$(find "$1" -type f)
        while read -r item
        do
            ret+=($item)
        done <<< "$links"
        while read -r item
        do
            ret+=($item)
        done <<< "$normal_files"
        echo ${ret[@]}
}


# check if two arguments are given
if [ $# -ne 2 ]; then
  echo "Usage: $0 oldfolder newfolder"
  exit 1
fi

# check if the arguments are valid directories
if [ ! -d "$1" ] || [ ! -d "$2" ]; then
  echo "Invalid directories"
  exit 2
fi

# if it is ended by '/' delete '/'
inputemp=$1
if test "${inputemp: -1}" == "/";then
    s1=${inputemp%/}
else 
    s1=$inputemp
fi

inputemp=$2
if test "${inputemp: -1}" == "/";then
    s2=${inputemp%/}
else 
    s2=$inputemp
fi

 


# create a new directory for patch files
patch_dir="$(dirname $s1)"/patch_"$(echo  "$s1" | awk -F'/' '{print $NF}')"
echo  patch_path: $patch_dir 
mkdir -p "$patch_dir"

wddi="$(dirname $s1)"/"$wddi"

if [ ! -f $wddi ];then
  touch "$wddi"
else 
  > $wddi 
fi

# sync in new target
all_files=`GetAllfile $s1`
all_files=$(echo  -n "$all_files" | tr ' ' '\n')
echo "$all_files" | while read oldfile; do
  # get the relative path of the file
  rel_path=${oldfile#$s1/}
  # get the corresponding file in the second directory
  newfile="$s2/$rel_path"
  # exist in old and not exist in new and create same name to instead in the new folder 
  if [ ! -f "$newfile" ]; then
    echo -e "\033[0;36m [disapper in new]: $newfile Generate 0 Bytes to instead in new target \033[0m"

  #appends not exist path recorded list
    if [ ! -d "$(dirname $newfile)" ];then
         mkdir -p "$(dirname $newfile)"
         echo "$(dirname $rel_path)" >> $wddi    
    fi
  #write 0 bytes
     > $newfile  
  fi

done


# sync in old target
all_files=`GetAllfile $s2`
all_files=$(echo  -n "$all_files" | tr ' ' '\n')
echo "$all_files" | while read newfile; do
  # get the relative path of the file
  rel_path=${newfile#$s2/}
  # get the corresponding file in the second directory
  oldfile="$s1/$rel_path"
  # exist in new and not exist in old and create same name to instead in the old folder 
  if [ ! -f "$oldfile" ]; then
  echo -e "\033[0;36m [disapper in old]: $oldfile Generate 0 Bytes to instead in old target  \033[0m"
  #  create the parent directory if needed
    mkdir -p "$(dirname $oldfile)"
    > $oldfile  
  fi

done


# Generate patch 
# when running  in this poision ,old target is same as new target
all_files=`GetAllfile $s1`
all_files=$(echo  -n $all_files | tr ' ' '\n')
echo "$all_files"  | while read oldfile; do
  # get the relative path of the file
  rel_path=${oldfile#$s1/}
  # get the corresponding file in the second directory
  newfile="$s2/$rel_path"
  # Haved sync and create the patch file name
  patch_file="$patch_dir/$rel_path.patch"
  # create the parent directory if needed
  mkdir -p "$(dirname "$patch_file")"
  # use bsdiff to generate the patch file
  oldmd5=$(md5sum $oldfile | awk '{print $1}')
  newmd5=$(md5sum $newfile | awk '{print $1}')

  if [ "$oldmd5" = "$newmd5" ]; then
      
  echo -e "\033[0;36m Don't Need to Change \033[0m"

  else
      bsdiff "$oldfile" "$newfile" "$patch_file"
      echo -e "\033[0;36mGenerated patch for $rel_path \033[0m"
  fi
done


# after generating patchs_files ,delete extra 0 bytes files by delete index
if [ ! -f $wddi ];
then 
    exit 1
fi
cat $wddi | while read del_item;
do
    realpath=$s2/$del_item
    if [  -d "$realpath" ] ;then
        echo $realpath
        rm -rf $realpath
    fi
done

echo "Done. Patch files are in $patch_dir"

upgrate.sh

#!/bin/bash

# wdfi="waiting_for_delete"
wddi="waiting_for_dindex"
# check if two arguments are given
if [ $# -ne 2 ]; then
  echo "Usage: upgrade  oldfolder  patch_path "
  echo "example ./upgrade   ./test/old/ ./test/boot/"
  exit 1
fi



# check if the arguments are valid directories
if [ ! -d "$1" -o  ! -d "$2" ] ; then
  echo "Invalid directories"
  exit 2
fi

# if it is ended by '/' delete '/' => old folder 
inputemp=$1
if test "${inputemp: -1}" == "/";then
    s1=${inputemp%/}
else 
    s1=$inputemp
fi

# if it is ended by '/' delete '/'=> patch_path
inputemp=$2
if test "${inputemp: -1}" == "/";then
    patch_path=${inputemp%/}
else 
    patch_path=$inputemp
fi 

wddi="$patch_path/$wddi"
echo    wddi : "$wddi"

patch_path="$patch_path"/patch_"$(echo  "$s1" | awk -F'/' '{print $NF}')"
echo find patch_path:  $patch_path

 

#loop item in path_item
find "$patch_path" -type f -name "*.patch" | while read patch_item; do
    temp=${patch_item#$patch_path/}
    temp=${temp%.patch}  #equal to   temp=${temp:0:${#temp}-6}
    oldfile="$s1/$temp"
    newfile="$s1/$temp"
    mkdir -p "$(dirname "$newfile")"
    if [ ! -f "$newfile" ];
    then
    > $newfile
    fi
    echo -e "\033[0;36m Generate $oldfile $newfile \033[0m"
    
# execute bspatch
    bspatch "$oldfile" "$newfile" "$patch_item"
done


#delete not exist index when  processing  to delete 
if [ ! -f $wddi ];
then 
    exit 1
fi
cat $wddi | while read del_item;
do
    realpath=$s1/$del_item
    if [  -d "$realpath" ] ;then
        echo $realpath
        rm -rf $realpath
    fi
done
echo "done"

执行分析

这样在服务端上执行

./gen  ./test/old  ./test/new (old是旧版本,new为新版本),则在test下生成patch_old,和waiting_for_dindex.

./upgrade  旧文件位置  pathch文件父路径(不包括pathch文件名)文章来源地址https://www.toymoban.com/news/detail-730181.html

注意事项和关注的地方:
  1. 在生成patch文件的时候,也就是 ./gen 脚本 ,先让旧目录和新目录进行了同步,最终根据每一个文件生成patch文件,并把这些patch文件单独放在pathch_旧目录名 这个目录下,这个目录和旧目录(新目录有相同的目录结构.
  2. 在./gen脚本中,因为做了同步,如果存在新版本中删除旧目录,则会将被删除的旧目录同步到新目录中,并把被删除的旧目录存放在一个清单列表中(如wddi),最终在打完patch后会在服务器端删除这些同步的内容.
  3. ./upgrade脚本 接受传入一个旧目录,和一个patch目录的父路径(不包括patch目录名),这样会在patch的父目录中寻找以旧目录后缀结尾的patch目录,然后遍历这个patch目录依次调用bspatch升级, 最终在客户端调用升级完成后会根据清单去删除同步过程中产生旧目录,0字节的文件.

到了这里,关于Bsdiff,Bspatch 的差分增量升级(基于Win和Linux)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux升级之路】3_Linux进程概念

    🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux升级之路】 ✒️✒️本篇内容:认识冯诺依曼系统,操作系统概念与定位,深入理解进程概念(了解PCB),学习进程状态(创建进程、僵尸进程和孤儿进程),进程优先级进程切换(进程竞争性与独立性、并行与并

    2024年02月05日
    浏览(34)
  • Linux升级openssl版本

    公司由于使用第三方漏洞扫描,检测出openssh存在漏洞以及ssl漏洞 因次得升级openssl 升级步骤: 1、去官网下载最新版本,或wget下载也行 http://www.openssl.org wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz 2、解压并进入解压目录后执行: ./config --prefix=/usr/local/openssl shared zlib make de

    2023年04月21日
    浏览(44)
  • linux系统openssh升级

    说明 整个过程不需要卸载原先的openssl包和openssh的rpm包。本文的环境都是系统自带的openssh,没有经历过手动编译安装方式。如果之前有手动编译安装过openssh,请参照本文自行测试是否能成功。 如果严格参照本文操作,保证你升级没问题 一、安装依赖包 二、下载openssh对应得

    2024年02月14日
    浏览(36)
  • linux升级git版本

    想要在linux上更新git版本,我查了很多资料,但是似乎都需要先把旧版本git删掉然后再进行更新 1、查看git版本 查看git当前版本 2、安装依赖 源代码安装和编译git,需要安装依赖,具体命令: 3、卸载旧版本的git 不把旧版本卸载掉的话会导致升级失败 4、打开文件夹 选择/usr/local/src文

    2024年02月08日
    浏览(36)
  • 『Linux升级路』权限管理

     🔥 博客主页: 小王又困了 📚 系列专栏: Linux 🌟 人之为学,不日近则日退  ❤️ 感谢大家点赞👍收藏⭐评论✍️ 目录 一、Linux中的用户 📒1.1Linux用户分类 📒1.2用户转换 📒1.3指令提权 二、Linux权限管理 📒2.1权限的概念 📒2.2Linux中的角色 📒2.3文件权限 📒2.4权限修

    2024年02月08日
    浏览(35)
  • Linux Openssl升级(详)

    环境: 系统版本:CentOS Linux release 7.4.1708(Core) 内核版本:3.10.0-514.el7.x86 64 Openssl 由1.0.2k-fips 升级到3.1.0-beta1 升级说明: OpenSSL缓冲区溢出漏洞(CVE-2021-3711),OpenSSL = 1.1.1k OpenSSL缓冲区溢出漏洞(CVE-2021-3712),OpenSSL = 1.1.1k或OpenSSL = 1.0.2y Openssl源码地址 OPENSSL 源码包下载地址:https:/

    2024年02月15日
    浏览(35)
  • Linux Openssh 升级(详)

    环境: 系统版本:CentOS Linux release 7.4.1708(Core) Openssh 由6.6.6p1升级到9.2p1 升级说明: OpensSSH依赖于OpenSSL, 源码包下载地址:https://mirrors.aliyun.com/pub/OpenBSD/OpenSSH/portable/ OpenSSH sshd 缓冲区错误漏洞(CVE-2016-10012),OpenSSH 7.4 OpenSSH 代码问题漏洞(CVE-2016-10009),OpenSSH 7.4 OpenSSH 资源管理错误

    2024年02月13日
    浏览(34)
  • OpenSSH升级(linux 6)

    一 说明     本文档为Linux6.x Openssh升级至8.4文档。 二 使用打包好的 Openssh rpm 包 进行升级 2.1 安装 telnet 服务 升级Openssh服务之前,确保telnet可以登录系统。 安装步骤如下: rpm -ivh xinetd-2.3.14-29.el6.x86_64.rpm rpm -ivh telnet-0.17-46.el6.x86_64.rpm rpm -ivh telnet-server-0.17-46.el6.x86_64.rpm 2.2 升

    2024年04月15日
    浏览(39)
  • Linux下Nginx升级

      nginx版本升级不会覆盖配置文件,但以防万一升级前请先备份配置文件或者配置文件夹 默认配置文件地址:/usr/local/nginx/conf/nginx.conf    1.下载   2.解压  3.nginx配置编译  注意: 如果历史版本nginx路径不是默认路径,需要在 ./configure中追加上对应参数,默认为/usr/local/nginx。

    2024年02月16日
    浏览(28)
  • 【Linux】升级openssl版本

    目录 摘要 准备工作 1.查看openssl的版本 2.查看openssl的路径 3.备份openssl文件 4.下载openssl 升级openssl 1.解压openssl 2.切换到解压好的openssl目录 3.配置openssl安装目录 4.编译安装 5.创建软链接 6.添加动态链接库数据 7.更新动态链接库:ldconfig -v 8.验证openssl 为什么要升级openssl版本,一

    2024年04月23日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包