国外开发者:检验并恢复损坏的git数据文件

开发 前端
最近我提交了一分损坏的文件到资源库上,被问到数据是否可恢复。根据提出的描述开始着手调查和修复问题,我认为其他人也会对解决的过程感兴趣,或者遇到相同状况后可以帮助到你。

最近我提交了一分损坏的文件到资源库上,被问到数据是否可恢复。根据提出的描述开始着手调查和修复问题,我认为其他人也会对解决的过程感兴趣,或者遇到相同状况后可以帮助到你。

我开启了检查(fsck),发现问题出现在一个类上(我过去经常用 $pack $obj 命令使得输出可读,而且因为我也更倾向于他们):

  1. $ git fsck 
  2. error: $pack SHA1 checksum mismatch 
  3. error: index CRC mismatch for object $obj from $pack at offset 51653873 
  4. error: inflate: data stream error (incorrect data check) 
  5. error: cannot unpack $obj from $pack at offset 51653873 

错误的信息意味着一个字节在某一个地方混乱了,推测是类里面描述过的(因为检验和的索引和zlib都失败了)

通过阅读zlib源代码,我发现“检查不正确数据”意味着adler-32

算法检验zlib数据末端没有匹配已经增加的数据。因此通过zlib压缩数据是没有用的,因为到每次末尾都会有错误,此时我们了也解到这个crc文件不能匹配。这个出错的字节存在类文件的任何地方。

第一件事情我从packfilepull出损坏的文件。我需要知道到底它多大的类,然后我发现了下面的信息:

  1. $ git show-index <$idx | cut -d' ' -f1 | sort -n | grep -A1 51653873 
  2.   
  3. 51653873 
  4. 51664736 

Show-index命令可以列出类和他们的偏移量。我们除了偏移量以外通通抛弃,然后排列,这样我们感兴趣的偏移量(这个位置是从上面fsck命令得出来的)是遵循下一个对象的偏移量。现在我们知道类文件长度是10863字节,然后我们可以抓取它:

  1. dd if=$pack of=object bs=1 skip=51653873 count=10863 

我检查了文件的十六进制,搜索任何明显的错误(例如,一个4K大小运行中的0是文件系统冲突很好的信号)。但是所有的地方看起来都很合理。

注意到“object”文件不适合被zlib直接压缩;它本身包含git类信息头,而且是可变长度的。我们想要去掉它,所以开始直接使用zlib操 作数据。你可以用你手工的方式(格式化信息的描述在Documentation/technical/pack-format.txt里面),或者你也可 以用debugger搞定它。我选择了后者,创建了一个验证的包如下:# pack magic and version

  1. # pack magic and version 
  2. printf 'PACK\0\0\0\2' >tmp.pack 
  3. # pack has one object 
  4. printf '\0\0\0\1' >>tmp.pack 
  5. # now add our object data 
  6. cat object >>tmp.pack 
  7. # and then append the pack trailer 
  8. /path/to/git.git/test-sha1 -b <tmp.pack >trailer 
  9. cat trailer >>tmp.pack 

然后在debugger中运行”git index-pack tmp.pack” 命令(会停在unpack_raw_entry)。做到这里,我发现有3个自己的信息头(信息头本身有一个完整的类型和大小)。然后我用下面语句去掉了这些内容:

  1. dd if=object of=zlib bs=1 skip=3 

我用一个自定义的C程序过的zlib的解压锁结果。但是它报错了,我得到准确的输出数字字节(也就是说,它匹配了上面解码的git的信息头大小)。 但是”git hash-object” 命令的结果并没有相同的shal值。所以现在仍然有一些错误的字节是我不知道的。这个文件发生在C源程序代码, 我希望我能注意到一些明显的错误,但是我没有成功,我甚至都编译成功了!

我也试过和在资源库相同路径下其他版本的文件作比较,希望有不一样和不合理的部分。不幸运的是,这碰巧是唯一的修订文件,在这个资源库中。所以我没有任何东西可以作比较。

于是我采取不同的措施,猜测着冲突是由限制单个字节引起的,我写了交换每个字节的程序,是这解压缩得到结果。因为这个类文件压缩过后仅有10K,花了很多时间解压出之后的结果是2.5M。

这是我用的程序:

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #include <string.h> 
  4. #include <signal.h> 
  5. #include <zlib.h> 
  6.   
  7. static int try_zlib(unsigned char *buf, int len) 
  8.   /* make this absurdly large so we don't have to loop */ 
  9.   static unsigned char out[1024*1024]; 
  10.   z_stream z; 
  11.   int ret; 
  12.   
  13.   memset(&z, 0, sizeof(z)); 
  14.   inflateInit(&z); 
  15.   
  16.   z.next_in = buf
  17.   z.avail_in = len
  18.   z.next_out = out; 
  19.   z.avail_out = sizeof(out); 
  20.   
  21.   ret = inflate(&z, 0); 
  22.   inflateEnd(&z); 
  23.   return ret >= 0; 
  24.   
  25. /* eye candy */ 
  26. static int counter = 0
  27. static void progress(int sig) 
  28.   fprintf(stderr, "\r%d", counter); 
  29.   alarm(1); 
  30.   
  31. int main(void) 
  32.   /* oversized so we can read the whole buffer in */ 
  33.   unsigned char buf[1024*1024]; 
  34.   int len; 
  35.   unsigned i, j; 
  36.   
  37.   signal(SIGALRM, progress); 
  38.   alarm(1); 
  39.   
  40.   len = read(0, buf, sizeof(buf)); 
  41.   for (i = 0; i < len; i++) { 
  42.     unsigned char c = buf[i]; 
  43.     for (j = 0; j <= 0xff; j++) { 
  44.       buf[i] = j; 
  45.   
  46.       counter++; 
  47.       if (try_zlib(buf, len)) 
  48.         printf("i=%d, j=%x\n", i, j); 
  49.     } 
  50.     buf[i] = c; 
  51.   } 
  52.   
  53.   alarm(0); 
  54.   fprintf(stderr, "\n"); 
  55.   return 0; 

编译和运行的结果:

  1. gcc -Wall -Werror -O3 munge.c -o munge -lz 
  2. ./munge <zlib 

有一些错误出现(如果你在zlib的信息头中得到”no data”信息,zlib认为它运行的很好:))。但是我中途得到了下面的信息:

  1. i=5642j=c7 

等到运行完结束,末尾获得了更多的记录(整理crc文件匹配了我们损坏的文件)。有一个很好的机会,在中间的记录,就是源代码的问题

在一个十六进制编辑器中对字节稍微做了一些调整,zlib解压缩(毫无错误!),然后管道输出”git hash-object”,报告了损坏文件的shal值,成功了!

我修正packfile文件本身:

  1. chmod +w $pack 
  2. printf '\xc7' | dd of=$pack bs=1 seek=51659518 conv=notrunc 
  3. chmod -w $pack 

发现’\xc7′来自与替换我们的“munge”程序。把原来的对象偏移(51653873)导出了偏移量51659518 。通过“munge”(5642)增加了代替部分的偏移量,然后增加了之前去掉的3字节的git信息头。

最后,”git fsck” 清理干净。

关于冲突本身来说,我很幸运它确实是一个字节。实际上,证明就是单独的位。0xc7字节发生冲突成为0xc5.所以推测由错误的硬件引起,或者物理射线。

紧接着,中止关注于解压缩的输出是错的吗?我本会一直看前面的部分这样永远不会发现它。下面是解压缩后冲突数据的不同,真正的数据文件:

  1. -       cp = strtok (arg, "+"); 
  2. +       cp = strtok (arg, "."); 

调整了一个字节,最终仍然是有效的,可读的C碰巧做了完全不同的事情!一次不同尝试会造成幸运的日子,看看zlib的输出可能确实有用,正如大多数随机的改变也许就会破坏C代码。

但更重要的是,git的hash、检验和引起了在另外一个系统中,很容易不被检测问题。这个结果仍然会编译,但是可能就引起一个有趣的bug(归咎于一些随机性的提交)

原文链接:http://thread.gmane.org/gmane.comp.version-control.git/236238

译文链接:http://blog.jobbole.com/50108/

责任编辑:陈四芳 来源: 伯乐在线
相关推荐

2014-06-04 09:21:07

Swift开发语言

2011-08-29 16:41:14

OracleRMAN恢复数据文件的恢复

2024-03-07 08:55:24

JavaPython

2011-03-22 16:20:19

恢复数据库

2012-06-13 01:23:30

开发者程序员

2009-07-02 19:07:25

Linux

2009-06-30 10:40:28

Linux

2010-02-03 09:06:26

Java EE 6

2015-09-08 09:55:28

手游设计资源

2023-07-07 08:15:18

JavaPython编写

2013-08-07 10:04:37

MySQL数据恢复

2011-11-28 13:33:41

iOS

2011-05-17 11:33:43

oracle数据库

2014-06-04 11:25:39

Swift苹果iOS

2020-12-02 10:02:01

MacLinux苹果

2010-05-06 09:42:28

Oracle表空间

2017-04-01 18:00:08

开发者数据库

2013-11-07 11:23:13

2022-12-01 14:02:02

MySQL数据文件

2010-07-13 14:09:07

SQL Server数
点赞
收藏

51CTO技术栈公众号