• 一个由硬链接引发的问题


    一个由硬链接引发的问题

    问题背景

    最近关于 Fastjson 的漏洞又被爆出来了,作为修理工(哦不,专业的软件工程师),又到了我们表演的时候了。

    我们有很多服务是用的老版本的有漏洞的jar包,为了解决这个漏洞问题,我们决定来个偷梁换柱,使用新版本的jar包直接把老版本的有漏洞的jar版本直接覆盖掉。哎哎哎,要想程序搞得好,三十六计不可少。

    问题现象

    既然方案确定了,那就开始干,具体思路如下:

    • 从环境上找出老版本的jar包,然后备份到一个备份路径下,这里备份是为了支持回退的功能。
    • 然后把新版本的jar包拷贝到老版本jar包的路径下。
    • 重启所有的微服务是jar包生效。

    代码的逻辑如下图所示:

    for 老版本的jar包 in 所有老版本的jar包; do
         备份老版本的jar包
         将新版本的jar包拷贝覆盖老版本的jar包
    done
    
    • 1
    • 2
    • 3
    • 4

    整体思路很简单,但是问题就出在这个备份上面。在实际的测试过程中,发现备份的目录下,jar包的版本大多数都变成新版本的jar包了,而不是老版本的jar包,如下图所示,在 /tmp/home/testuser 下的 fastjson-1.2.70.jar 这个包的 Hash 值实际上和 fastjson-1.2.83.jar 这个包的 Hash 值一样。

    最开始一直以为是自己代码逻辑写的有问题,于是一遍一遍的检查。我先把新版本的jar包覆盖老版本的 jar 包这个逻辑去掉,然后计算备份路径下所有的 Hash 值,发现都是老版本的 jar 包,是没有问题的。

    但是一旦我把覆盖老版本的 jar 包逻辑加上去,这个备份的 jar 包版本就变了,我反反复复搞了好几次结果都是一样的,真是让我百思不得其解。困扰程序员的两大难题之一在我脑海中产生了,它为啥就跑不起来呢?

    既然看不出来,那就只能使用程序员的必杀器了,Debug 一把。我把每次循环执行覆盖操作后,备份目录的下的 jar 的 Hash 值都算了一下,结果发现在执行第二次循环的时候,备份目录的下的 jar 文件就已经变成了新版本的jar包文件了。

    观察到这个现象之后,我就开始思考为啥会出现这种情况呢?第二个循环的逻辑明明和第一次的逻辑是一样的,为啥结果就变了呢?

    当天搞了很久也没有找到原因是啥,脑壳都给我搞昏了。没办法,明天接着搞。第二天早上来,由于经过了一晚上的回血,思路又清晰起来了,觉得自己又行了。

    这个时候我想起来,之前有人好像给我说过,我们的jar包是做的硬链接的。这个让我想到了一个经典的面试题:Linux 的软链接和硬链接的区别是啥?区别是啥来着?嗯嗯……,想不起来了,书到用时方恨少啊,默默的流下了没有知识的泪水。

    网上找了一些文章来看,又从电脑屏幕下把压着的《鸟哥的 Linux 私房菜》取出来复习一下。

    然后登录到环境上,看了一下环境上老版本的 jar 包,发现确实是做的硬链接。为了演示这个情况,我在我自己的环境上复现了一下这种情况,如下图所示:

    软链接和硬链接的区别可以参考网上的资料和书籍,我在这里就不细讲了。针对我当前遇到的这个问题,可以打个不恰当的比喻:硬链接就好像你照镜子一样,你做了什么样的改变,镜子里面的你也会做同样的改变。如上图所示,当我们改变 /home/testuser 目录下的 fastjson-1.2.70.jar 这个文件的内容时,/home/admin 目录下的 fastjson-1.2.70.jar 文件的内容会变得和它一样。

    知道了这个特性,我们再结合代码来分析一下:

    • 已知条件:环境上有两个jar包文件: /home/testuser/fastjson-1.2.70.jar/home/admin/fastjson-1.2.70.jar 这两个jar包是硬链接关系

    • 推导:当我们执行第一次for循环到将新版本的jar包拷贝覆盖老版本的jar包这个步骤时,会把其中一个路径下的jar包版本替换成新版本的jar包了,这个时候相当于/home/testuser/fastjson-1.2.70.jar/home/admin/fastjson-1.2.70.jar 这两个jar包都已经变成新版本的jar包了;当第二次for循环到备份老版本的jar包时,这个时候备份的是实际上已经是新版本的jar包了。这个就是为啥第二次以后备份的jar包版本都是新版本的原因了。

    for 老版本的jar包 in 所有老版本的jar包; do
         备份老版本的jar包
         将新版本的jar包拷贝覆盖老版本的jar包
    done
    
    • 1
    • 2
    • 3
    • 4

    解决方法

    知道原因了,我们就思考上面的代码应该怎么改?很显然,备份和拷贝覆盖老版本的jar包这两个动作应该拆分到两个for循环中去实现才行,这样的备份这个for循环里面就会备份的是所有老版本的jar包。如下面的代码所示:

    for 老版本的jar包 in 所有老版本的jar包; do
         备份老版本的jar包
    done
    for 老版本的jar包 in 所有老版本的jar包; do
         将新版本的jar包拷贝覆盖老版本的jar包
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这个问题的解决主要是要理解硬链接的原理。说实话这个知识之前也看过很多遍,每次看的时候都觉得自己懂了,但是真正遇到问题的时候,才发现自己根本就没有懂。

    通过这次问题的解决,我想我应该是对硬链接有了一定的了解。其实在日常的工作中,很多的小问题背后其实都对应的一些知识点的,如果我们能够把这些小问题搞清楚,其实对我们真正理解和掌握这个知识是有很大帮助的。好了,今天就到这里了,朋友们,我们下期再见!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8w7NZXGN-1656244629225)(https://raw.githubusercontent.com/sparkchans/pictures/master/%E5%86%8D%E8%A7%81.gif)]

  • 相关阅读:
    【自动驾驶】PETR/PETRv2/StreamPETR论文分析
    奇瑞新能源旗下新车——无界Pro上市,雷霆机甲炫酷登场
    C++ Tutorials: C++ Language: Classes: Classes (I)
    文件IO-缓冲区
    【开学季征文】即将入学,谈谈我对计算机专业的认识
    Axios使用笔记
    宝塔服务器之堡塔应用管理器的使用【php消息队列】
    华为云Stack的学习(五)
    如何把arguments转换为数组
    基础SQL DML-插入语句
  • 原文地址:https://blog.csdn.net/m0_37890535/article/details/125473567