ZFS文件系统数据恢复的方法

本文介绍了当ZFS文件系统出现问题时如何进行数据恢复。首先,尝试使用`zpool clear -F`或`zpool import -F`命令恢复,若无效则考虑在只读模式下导入受损池。如果以上方法都失败,需要手动解析元数据并编写脚本来恢复文件。文中提供了使用zdb工具和Python实现的详细步骤。
部署运行你感兴趣的模型镜像

前言

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

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值