因为误操作,rm -rf 删掉了盘里的全部文件, 使用 ext4magic /dev/sdb1 -M -d /backup 恢复文件。
wget ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/robi-1/CentOS_CentOS-6/x86_64/ext4magic-0.3.1-1.2.x86_64.rpm
rpm -ivh ext4magic-0.3.1-1.2.x86_64.rpm
wget https://downloads.sourceforge.net/project/extundelete/extundelete/0.2.4/extundelete-0.2.4.tar.bz2
tar -xjf extundelete-0.2.4.tar.bz2 && cd extundelete*
./configure
make && make install
有个ssdb 数据库,恢复后启动提示 259 file missed. 同时提示了文件名, touch 建立这样一个空文件,按照提示,建立了200多个空文件,结果ssdb能启动了,可以get hget hscan,但是数据会不完全,赶紧把数据到走。
ext4magic 的恢复效果比 extundelete 能恢复出更多文件。ext4magic 除了按照原目录结构恢复出来了,还恢复出来了100多个 32M 的未知文件名的文件,从大小看,应该是 leveldb的一个 sst 文件。
花了大量时间研究leveldb的文件数据结构,试图自己写程序能够导出单个sst里面的数据。后来找到 https://antimatter15.com/2015/12/recovering-deleted-data-from-leveldb/
按照 http://blog.csdn.net/gold2008/article/details/69948892 安装 go 语言环境
go get github.com/golang/leveldb
cd /root/go/src/github.com/golang/leveldb/cmd/ldbdump
go build -o ldbdump main.go
cp ldbdump /usr/bin/
然后 ldbdump xxxxxx.ldb > raw.file 可以发现 raw.file 里面是明文字符了,但是如果原来的数据含有中文或者二进制内容,那么导出文件不好用php处理,于是对 main.go 进行了一点修改:
package main
import (
"bytes"
"flag"
"fmt"
"os"
"encoding/base64"
"github.com/golang/leveldb/db"
"github.com/golang/leveldb/table"
)
var (
verifyChecksums = flag.Bool("c", false, "Verify checksums.")
truncate = flag.Bool("t", false, "Truncate long keys and values.")
kBuf, vBuf bytes.Buffer
)
func main() {
flag.Parse()
bad := false
for i, arg := range flag.Args() {
if i != 0 {
fmt.Println()
}
//fmt.Printf("filename: %q\n", arg)
if err := dump(arg); err != nil {
fmt.Printf("error: %q\n", err)
bad = true
}
}
if bad {
os.Exit(1)
}
}
func byteString(p []byte) string {
for i := 0; i < len(p); i++ {
if p[i] == 0 {
return string(p[0:i])
}
}
return string(p)
}
func dump(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
// No need to "defer f.Close()", as closing r will close f.
r := table.NewReader(f, &db.Options{
VerifyChecksums: *verifyChecksums,
})
defer r.Close()
t := r.Find(nil, nil)
for t.Next() {
k, v := t.Key(), t.Value()
if *truncate {
k = trunc(&kBuf, k)
v = trunc(&vBuf, v)
}
enc := base64.StdEncoding.EncodeToString([]byte(v))
fmt.Printf("%q\t%v\n", k, enc)
}
return t.Close()
}
func trunc(dst *bytes.Buffer, b []byte) []byte {
if len(b) < 64 {
return b
}
dst.Reset()
fmt.Fprintf(dst, "%s...(%d bytes)...%s", b[:20], len(b)-40, b[len(b)-20:])
return dst.Bytes()
}
这样导出文件里面就是一行行的了,value 是 base64 编码的,用php可以直接处理。 ssdb 把hash 存入leveldb的时候,key是按照 h\thash=key\x 这样的格式存的,可以很容易提取出 hash 名和 key。这样又恢复 了一部分数据。整个过程花费了大量时间,以后都要在另一台电脑搞备份了,免得再有这样的灾难。