仔細看了一下,和原先弱智大哥的精華貼不太一樣,特貼出,原作出自水木清華,有點老了,針對2.2.x kernel的........
---------------------------------------------------------------
usr/doc/HOWTO/mini/Ext2fs-Undeletion.gz
在此嚴正聲明!! 寫這篇文章的目的,是給那些處於萬不得已情況下的人們,有一個挽回的機會,並不意味著從此我們就可以大意,砍檔不需要三思。前面提到,我有一個檔案無法 100% 救回,事實上,長達 8MB 的檔案能救回 99% 已是幸運中的幸運,一般的情況下若能救回 70% - 80% 已經要愉笑了。所以,不要指望 undelete 能救回一切。預防勝於治療! 請大家平時就養成好習慣,砍檔前請三思!!!
理論分析
我們能救回的機會有多大? 在 kernel-2.0.X 系列中 (本站所用的 kernel 是 2.0.33) ,取決以下兩點:
檔案原來所在的磁區是否沒有被覆寫?
檔案是否完全連續?
第一點我們可以與時間競賽,就是當一發現檔案誤砍時,要以最快的速度 umount 該 filesystem, 或將該 filesystem remount 成唯讀。就這次的情況而言,檔案誤砍是在事發一個小時後才發現的,但由於該 filesystem 寫入的機會很少 (我幾乎可確定一天才只有一次,做 backup),所以第一點算是過關了。
第二點真的是要聽天由命了,就本站所使用的 kernel, 必須要在假設「長檔案」所占的 block 完全連續的情況下,才有可能完全救回來! 一個 block 是 1024 bytes,長達 8 MB 的檔案就有超過 8000 個 block。在經常讀寫的 filesystem 中,可以想見長檔案很難完全連續,但在我們的系統中,這一點似乎又多了幾分指望。同時,Linux ext2 如此精良的 filesystem, 能做到前 7950 多個 block 都連續,這一點也功不可沒。
好了,以下我就講一下我的步驟。
搶救步驟 I - mount filesystem readonly
該檔案的位置原來是在 /var/hda/backup/home/bbs 下,我們系統的 filesystem 組態是:
root@bbs:/home/FTP/rescue# df
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/sda1 396500 312769 63250 83% /
/dev/sda3 777410 537633 199615 73% /home
/dev/hda1 199047 36927 151840 20% /var/hda
/dev/hda2 1029023 490998 485710 50% /home/ftp
因此 /var/hda 這個 filesystem 要馬上 mount 成 readonly (以下請用 root 身份):
mount -o remount,ro /var/hda
當然也可以直接 umount 它,但有時候可能有某些 process 正在此 filesystem下運作,您可能無法直接 umount 它。因此我選擇 mount readonly。但您也可以用:
fuser -v -m /usr
看一下目前是那些 process 在用這個 filesystem, 然後一一砍掉,再 umount。
搶救步驟 II
執行
echo lsdel debugfs /dev/hda1 less
看一下該 filesystem 最近被砍的 inode (檔案) 有那些 (為什麼是 /dev/hda1? 請見上頭的 df 列表)? 在這奶F檔案的重要資訊,如大小、時間、屬性等等。就我們的系統而言,其列示如下:
debugfs: 92 deleted inodes found.
Inode Owner Mode Size Blocks Time deleted
....................................................................
29771 0 100644 1255337 14/14 Sat Jan 30 22:37:10 1999
29772 0 100644 5161017 14/14 Sat Jan 30 22:37:10 1999
29773 0 100644 8220922 14/14 Sat Jan 30 22:37:10 1999
29774 0 100644 5431 6/6 Sat Jan 30 22:37:10 1999
請注意!我們必須要在檔案大小、被砍時間等資訊中判斷出要救回的檔案是那一個。在此,我們要救回 29773 這個 inode。
搶救步驟 III
執行
echo "stat <29773>" debugfs /dev/hda1
列出該 inode 的所有資訊,如下:
debugfs: stat <29773>
Inode: 29773 Type: regular Mode: 0644 Flags: 0x0 Version: 1
User: 0 Group: 0 Size: 8220922
File ACL: 0 Directory ACL: 0
Links: 0 Blockcount: 16124
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x36b31916 -- Sat Jan 30 22:37:10 1999
atime: 0x36aebee4 -- Wed Jan 27 15:23:16 1999
mtime: 0x36adec25 -- Wed Jan 27 00:24:05 1999
dtime: 0x36b31916 -- Sat Jan 30 22:37:10 1999
BLOCKS:
123134 123136 123137 123138 123140 131404 131405 131406
131407 131408 131409 131 410 131411 131668
TOTAL: 14
現在的重點是,必須將該 inode 所指的檔案,所指的 block 全部找回來。在這它?14 個 block? 不對啊! 應該要有 8000 多個 block 才對啊! 在這卯ilesystem 的「奧密」了。上頭所列的前 12 個 block 是真正指到檔案資料的 block, 稱之為 direct block 。第 13 個稱為第一階 indirect block, 第 14 個稱為第二階 indirect block 。什麼意思? 該檔的資料所在的 block 位置如下:
各位明白嗎? 第 13 個 (131411) 與第 14 個 block 其實不是 data, 而是 index,它指出接下來的 block 的位置。由於一個 block 的大小是 1024 bytes, 一個 int 在 32 位系統中是 4 bytes, 故一個 block 可以記錄 256 筆資料。以 131411 block 為例,它所記錄的資料即為 (在檔案未砍前):
131412 131413 131414 .... 131667 (共 256 筆)
而這 256 個 block 就真正記錄了檔案資料,所以我們稱為第一階。同理,第二階就有兩個層 index, 以 131668 來說,它可能記錄了:
131669 131926 132182 .... (最多有 256 筆)
而 131669 的 block 記錄為:
131670 131671 131672 .... 131925 (共 256 筆)
而這 256 個 block 才是真正儲存檔案資料的。而我們要的,就是這些真正儲存檔案資料的 block 。 理論上,我們只要將這些 index block 的內容全部讀出來,然後照這些 index 把所有的 block 全部讀到手,就能 100% 救回檔案 (假設這些 block 全部沒有被新檔案覆寫的話)。工程很大,但是可行。不幸的是,在 kernel-2.0.33, 其設計是,如果該檔案被砍了,則這些 index block 全部會規零,因此我所讀到的是
0 0 0 0 0 ..... (共 256 筆)
哇! 沒辦法知道這些 data block 真正所在的位置。所以,在此我們做了一個很大的假設: 整個檔案所在的 block 是連續的! 也就是我上頭的例子。這也就是為什麼說,只有連續 block (是指後頭的 indirect block) 的檔案才能完整救回,而這一點就要聽天由命了。
搶救步驟 IV
好了,現在我們只好假設所有的檔案處於連續的 block 上,現在請用http://archie.ncu.edu.tw/去找這個工具: fsgrab-1.2.tar.gz, 並將它安裝起來。因為步驟很簡單,故在此我就不多談。我們要用它將所需的 block 全部抓出來。它的用法如下:
fsgrab -c count -s skip device
其中 count 是只要 (連續) 讀幾個, skip 是指要從第幾個開始讀,例如我要從 131670 開始連續讀 256 個,就這樣下指令:
fsgrab -c 256 -s 131670 /dev/hda1 > recover
現在我們就開始救檔案吧! 以上頭的資料,我們必須用以下的指令來救: (注意到頭開的 12 個 block 並沒有完全連續!!!)
fsgrab -c 1 -s 123134 /dev/hda1 > recover
fsgrab -c 3 -s 123136 /dev/hda1 >> recover
fsgrab -c 1 -s 123140 /dev/hda1 >> recover
fsgrab -c 7 -s 131404 /dev/hda1 >> recover
這是開頭的 12 個 block, 對於第一階 indirect, 就資料來看好象是連續的 )
fsgrab -c 256 -s 131412 /dev/hda1 >> recover
注意要跳過 131411, 因為它是 index block。對於第二階 indirect, 我們 *假設* 它們都是連續的:
fsgrab -c 256 -s 131670 /dev/hda1 >> recover
fsgrab -c 256 -s 131927 /dev/hda1 >> recover
fsgrab -c 256 -s 132184 /dev/hda1 >> recover
............................................
要一直做,直到 recover 的大小超過我們所要救回的檔案大小 (8220922) 為止。要注意在這市 p心地跳過那些 index block (如 131668, 131669, 131926, 132183, ....) 了。
搶救步驟 V
最後一步,就是把檔案「剪」出來,並看看我們救回多少了。在這戊]我們重復上述步驟,弄出來的 recover 檔大小為 8294400,而我們要的大小是 8220922, 那就這樣下指令:
split -b 8220922 recover rec
則會做出兩個檔,一個是 recaa, 大小是 8220922, 另一個是 recab 則是剩下的大小,後者是垃圾,扔了即可。現在我們可以檢查這個檔案是不是「完整」的那個被誤砍的檔案了。由於我們的那個檔案是 .tar.gz 的格式,於是我們這個方法來檢查:
mv recaa recaa.tar.gz
zcat recaa.tar.gz > recaa.tar
如果沒有錯誤訊息,那表示成功了! 完全救回來了。但不幸的是,我們沒有成功,將弄出的 recaa.tar 改名再 gzip 之後,與原來的 recaa.tar.gz 比一下大小,發現少了 1%, 表示說該檔原來所在的 block 中最後有 1% 是不連續的 (或者被新寫入的檔案覆寫了),但這已是不幸中的大幸了。
後記
對於在 undelete 時 *必需* 假設所有 block 連續的問題,那份 HOWTO 文件說 Linus 與其它 kernel 設計者正著手研究,看能否克服這個困難,也就是在檔案砍掉時,不要將 index block 規零。我剛剛試一下 kenrel- 2.2.0 的環境,發現已做到了!! 以下是一個已砍的檔案的 inode data (由 debugfs 所讀出):
debugfs: Inode: 36154 Type: regular Mode: 0600 Flags: 0x0 Version: 1
User: 0 Group: 0 Size: 2165945
File ACL: 0 Directory ACL: 0
Links: 0 Blockcount: 4252
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x36b54c3b -- Mon Feb 1 14:39:55 1999
atime: 0x36b54c30 -- Mon Feb 1 14:39:44 1999
mtime: 0x36b54c30 -- Mon Feb 1 14:39:44 1999
dtime: 0x36b54c3b -- Mon Feb 1 14:39:55 1999
BLOCKS:
147740 147741 147742 147743 147744 147745 147746 147747 147748 147769
147770 157642 157643 157644 157645 157646 157647 157648 157649 157650
157651 157652 157653 157654 157655 157656 157657 157658 157659 157660
157661 157662 157663 157664 157665 157666 157667 157668 157669 157670
157671 157672 157673 157674 157675 157676 157677 157678 157679 157680
157681 157682 157683 157684 157685 157686 157687 1...................
.........9745 159746 159747 159748 159749 159750 159751 159752 159753
159754 159755 159756
TOTAL: 2126
真是太完美了!! 這意味著在 kernel-2.2.X 的環境下,我們不必假設所有的 block 都連續,而且可以百分之百找回所有砍掉的 block! 因此上述的第二個風險就不存在了。
【發表回復】【查看CU論壇原帖】【關閉】
sky-walker 回復於:2003-11-21 12:55:41
還有一個方法,是針對文本文件的,如E-mail....
在用戶不多的機器上,如果磁盤操作不是很頻繁,30分鐘內找到被刪除的內容還是很有希望的。如果文件內容還在磁盤上,可以用grep來幫助您找到文件,當然,這裡指的是文本格式的文件。例如,您不小心將一封E-mail刪掉了,請立即停止所有可能寫磁盤的活動,停止所有的存盤和編譯操作,然後切換到單用戶模式,如果您還不知道怎樣切換到單用戶模式,請參考init命令的幫助文件。然後卸載被刪除文件所在的文件系統。
---- 現在就可以用egrep來查找這個分區了,比如,信中包含字符串“
[email protected]”,而信放在/home/song/email下,且/home目錄掛在/dev/hda4上,於是運行命令:
---- # egrep -50 ‘
[email protected]’ /dev/hda4 > /tmp/x
---- 請確信/tmp/x不在/dev/hda4上後再按下回車。由於信比較短,所以只取前50行的內容。
---- 然後使用strings查看內容:
---- #strings /tmp/x less
---- 如果您足夠幸運且操作無誤,此時就可以找回丟失的信了。
147770 157642 157643 157644 157645 157646 157647 157648 157649 157650
157651 157652 157653 157654 157655 157656 157657 157658 157659 157660
157661 157662 157663 157664 157665 157666 157667 157668 157669 157670
157671 157672 157673 157674 157675 157676 157677 157678 157679 157680
157681 157682 157683 157684 157685 157686 157687 1...................
.........9745 159746 159747 159748 159749 159750 159751 159752 159753
159754 159755 159756
TOTAL: 2126
真是太完美了!! 這意味著在 kernel-2.2.X 的環境下,我們不必假設所有的 block 都連續,而且可以百分之百找回所有砍掉的 block! 因此上述的第二個風險就不存在了。
【發表回復】【查看CU論壇原帖】【關閉】
sky-walker 回復於:2003-11-21 12:55:41
還有一個方法,是針對文本文件的,如E-mail....
在用戶不多的機器上,如果磁盤操作不是很頻繁,30分鐘內找到被刪除的內容還是很有希望的。如果文件內容還在磁盤上,可以用grep來幫助您找到文件,當然,這裡指的是文本格式的文件。例如,您不小心將一封E-mail刪掉了,請立即停止所有可能寫磁盤的活動,停止所有的存盤和編譯操作,然後切換到單用戶模式,如果您還不知道怎樣切換到單用戶模式,請參考init命令的幫助文件。然後卸載被刪除文件所在的文件系統。
---- 現在就可以用egrep來查找這個分區了,比如,信中包含字符串“
[email protected]”,而信放在/home/song/email下,且/home目錄掛在/dev/hda4上,於是運行命令:
---- # egrep -50 ‘
[email protected]’ /dev/hda4 > /tmp/x
---- 請確信/tmp/x不在/dev/hda4上後再按下回車。由於信比較短,所以只取前50行的內容。
---- 然後使用strings查看內容:
---- #strings /tmp/x less
---- 如果您足夠幸運且操作無誤,此時就可以找回丟失的信了。
【發表回復】【查看CU論壇原帖】【關閉】
sky-walker 回復於:2003-11-21 12:55:41
還有一個方法,是針對文本文件的,如E-mail....
在用戶不多的機器上,如果磁盤操作不是很頻繁,30分鐘內找到被刪除的內容還是很有希望的。如果文件內容還在磁盤上,可以用grep來幫助您找到文件,當然,這裡指的是文本格式的文件。例如,您不小心將一封E-mail刪掉了,請立即停止所有可能寫磁盤的活動,停止所有的存盤和編譯操作,然後切換到單用戶模式,如果您還不知道怎樣切換到單用戶模式,請參考init命令的幫助文件。然後卸載被刪除文件所在的文件系統。
---- 現在就可以用egrep來查找這個分區了,比如,信中包含字符串“
[email protected]”,而信放在/home/song/email下,且/home目錄掛在/dev/hda4上,於是運行命令:
---- # egrep -50 ‘
[email protected]’ /dev/hda4 > /tmp/x
---- 請確信/tmp/x不在/dev/hda4上後再按下回車。由於信比較短,所以只取前50行的內容。
---- 然後使用strings查看內容:
---- #strings /tmp/x less
---- 如果您足夠幸運且操作無誤,此時就可以找回丟失的信了。