ZFS文件系统数据恢复的方法
前言
ZFS是一个非常稳定可靠的文件系统,但是还是有一定几率会出现系统坏掉,用户数据不能读取出来的情况。如果一个ZFS存储同时连接到2个控制器,两个控制器同时zpool import池,由于元数据覆盖,就会出现整个文件系统坏掉。
通常处理方法
首先应该参考官网:https://docs.oracle.com/cd/E26926_01/html/E25826/gbbwl.html#scrolltoc
其中修复 ZFS 存储池范围内的损坏的方法依次如下:
1.可以尝试使用 zpool clear -F 命令或 zpool import - F 命令恢复池。这些命令尝试回滚最后几次池事务,使其回到运行状态。可以使用 zpool status 命令查看损坏的池和建议的恢复步骤.
zpool clear -F tpool
zpool import tpool
zpool import -F tpool
2.您可以在只读模式下导入受损的池。此方法使您可以导入该池,从而可以访问数据。
zpool import -o readonly=on tpool
3.您可以使用 zpool import -m 命令导入缺少日志设备的池。
4.如果无法使用上述池恢复方法恢复池,则必须从备份副本中恢复池及其所有数据。所用的机制通常随池配置和备份策略的不同而有很大差别。首先,保存 zpool status 命令所显示的配置,以便在销毁池后可以重新创建它。然后,使用 zpool destroy -f 命令销毁池。此外,将描述数据集的布局和在本地设置的各种属性的文件保存在某个安全的位置(因为在使池无法访问后此信息将变得无法访问)。使用池配置和数据集布局,可以在销毁池后重新构造完整的配置。然后可以使用任何备份或恢复策略填充数据。
网上的资料介绍还有个尝试的选项:
1.zpool import -FX
2.zpool import -o readonly=on
3.The combination of zdb -lu and zpool import -T.
如果这些都不能把ZFS import进来,而用户又没有备份,这意味着数据的丢失。在这种情况下,是没有现成的工具可以恢复数据,只能依赖人工根据具体问题去编码,把磁盘上面的数据恢复出来。
恢复数据的方法
我们的思路是这样的:
1.使用zdb -e -vvvvvv zpoolname >> filename.txt
2.根据zdb运行过程中的错误,修改zdb源码,略过错误的元数据,尽量把元数据完整读取出来。源码中如果发现错误,就读取元数据信息时直接退出,我们应该注释掉该行。
3.根据元数据里面的文件信息和地址信息,写一个自动处理脚本,把文件读取出来,保存到新的存储空间。
使用的命令如下:
zdb_read -R -e -r 文件长度 pool2 元数据中的地址1:元数据中的地址2:元数据中的文件长度:f >> 新的文件路径
为了使zdb能够直接读取出文件,我们必须修改这个工具,直接截取文件长度大小。
Python的实现:
解析元数据:
def parse_metadata_blk(self):
self.file_path = ""
self.file_uid = ""
self.file_gid = ""
self.file_mode = 0
self.file_size = 0
self.blk_size = 0
self.blk_type = "unknown"
self.address_list = []
for line in self.lines:
if not line:
continue
var_list = line.split()
if len(var_list) <= 1:
#print "Error. can't parse: " + line
continue
if line.find("ZFS plain file (K=inherit) (Z=inherit)") != -1:
self.blk_type = "file"
continue
if line.find("ZFS directory (K=inherit) (Z=inherit)") != -1:
self.blk_type = "dir"
continue
if self.blk_type == "unknown":
continue
if var_list[0] == "path":
self.file_path = self.output_dir + var_list[1].strip()
continue
if var_list[0] == "uid":
self.file_uid = int(var_list[1].strip())
continue
if var_list[0] == "gid":
self.file_gid = int(var_list[1].strip())
continue
if var_list[0] == "mode":
self.file_mode = int(var_list[1].strip())
continue
if self.blk_type != "file":
continue
if var_list[0] == "size":
self.file_size = int(var_list[1].strip())
continue
if line.startswith("Indirect blocks:"):
start_address = True
continue
if len(var_list) >= 6 and var_list[1] == "L0":
self.address_list.append(var_list[2].strip())
self.blk_size_16 = var_list[3].split("L/")[0]
self.blk_size = int(var_list[3].split("L/")[0], 16)
continue
return 0
恢复文件:
def recover_file(self):
if not self.address_list:
return 0
if not self.file_path:
return 0
if os.path.exists(self.file_path):
os.remove(self.file_path)
print "The meta data is " + self.current_meta
print "Recovering file: %s size: %d" % (self.file_path, self.file_size)
ret = 1
for addr in self.address_list:
addr_list = addr.split(":")
cmd = "%s/zdb_read -R -e -r %d pool2 %s:%s:%s:f >> %s" % (self.pwdStr, self.file_size, addr_list[0], addr_list[1], self.blk_size_16, self.file_path)
try:
ret, out, err = self.run_system_cmd(cmd)
#ret = self.run_cmd(cmd)
except Exception, e:
print str(e)
return 1
self.file_size = self.file_size - self.blk_size
if ret != 0:
print "Failed to recover: " + self.file_path
return 1
else:
self.recover_file_num = self.recover_file_num + 1
# Update the file attribute.
try:
os.chmod(self.file_path, self.file_mode)
os.chown(self.file_path, self.file_uid, self.file_gid)
except Exception, e:
print str(e)
return 1
return 0
本文介绍了当ZFS文件系统出现问题时如何进行数据恢复。首先,尝试使用`zpool clear -F`或`zpool import -F`命令恢复,若无效则考虑在只读模式下导入受损池。如果以上方法都失败,需要手动解析元数据并编写脚本来恢复文件。文中提供了使用zdb工具和Python实现的详细步骤。
1180





