记一次误操作删除800G数据的经历

mattuy 2021年02月08日 289次浏览

前因后果

2021年2月5日,我正在尝试运行一份示例代码。该脚本类似这样:

# 这里有检查$REC_ROOT,但本脚本内并未处理,所以只会输出缺少环境变量$REC_ROOT,但继续执行
./config.sh
if [ ! -d $WAV_ROOT ]; then
  echo "Cannot find wav directory $WAV_ROOT"
  exit 1
fi

data="$REC_ROOT/data"

# 其他代码

if [ $stage -le 0 ]; then
  echo ""
  echo "Stage 0: Preparing data"
  rm -rf $data/*
  local/chime1_prepare_data.sh || exit 1
fi

由于脚本来自知名开源项目,我并没有仔细审查。另外由于对相关代码并不熟悉,我也没有正确配置相关环境变量,所以脚本中的WAV_ROOTREC_ROOT理所当然是未定义的。

我就这么冒失地执行了脚本,而它在打印两句警告后并没有停止执行,所以我认为环境变量并不是必须的,于是放任它继续执行。由于该脚本执行的是耗时任务,我将控制台隐藏到后台,去完成其他任务。

过了十几分钟,我收到一个应用程序的崩溃报告,因为相关文件不存在。我疑惑地检查,发现数百GB的数据已经不翼而飞。此时我才想起那个正在执行的脚本,切换过去后发现它还没有停止执行,正在疯狂删除我的文件。我赶忙杀掉了脚本,但包括由于没有root权限而删除失败的,原本800GB+的数据只剩5.6G。

让我们来看看发生了什么。

首先,脚本调用config.sh,检测到REC_ROOT环境变量不存在并打印警告。

然后脚本继续执行,if [ ! -d $WAV_ROOT ]; then这里是在检测WAV_ROOT是否是一个目录,如果不是,就退出脚本。按理说我并没有配置任何环境变量,此处应该退出。但bash脚本神奇地,当WAV_ROOT为空或者不存在时,这个检测会认为这是一个目录,从而通过检测。即:

unset NOT_EXIST
if [ -d $NOT_EXIST ]; then
  echo "this is a directory"
fi

上面的脚本是会输出的。

或许是因为参数为空时bash默认检测当前目录,以至于目录检测总是通过。

再然后,由于REC_ROOT未定义,$data=/data,然后相当于:

rm -rf /data/*

非常不巧和不幸的是,我将一块1TB的数据盘挂载在了/data上,于是迎来了降维打击。该数据盘中有800G+的数据,文件量大于10万,因此非常耐删,过了十几分钟还给我剩了几个G。而大量读写操作将数据恢复的难度推到了地狱级。

抢救措施

在杀掉脚本之后,我尝试卸载数据盘,但卸载失败,提示正忙。情急之下我忘记了可以通过正在运行的进程恢复它们打开的文件,而是想到先关机避免更多的读写。关机前发现VSCodium还在运行并且有未关闭的文件,于是抢救出几个正在编辑的代码文件。而这成了本次事故中我唯一抢救成功的文件。

之后,通过U盘刻录的系统修改原系统的配置,取消掉自动挂载数据盘,然后系统启动后以只读方式挂载数据盘尝试恢复数据。

正如前面所说,大量的读写操作让我失去了恢复的机会,尝试了不少恢复工具,但都没法扫描出目录结构,唯一可能有效的方法是通过特殊的文件头结构进行特征扫描,但这只能恢复一些特殊格式的文件,而对我最重要的都是纯文本数据,至于一些视频文件,由于尺寸太大数据分散在不同的块,也是基本没戏的。

还好,云端备份让我不至于一无所有,但还是痛失最近一个月的活动数据和大量不可描述之物。

总结

数据无价,谨慎操作。备份得当,也别太浪。