Linux 網絡文件系統(tǒng)的數據備份及恢復機制實現(xiàn)
由于主文件服務器一般需要處理文件的讀寫請求,寫請求僅占一部分,需要同步而執(zhí)行的操作造成的負載要小于主服務器,因此可能在較短的時間內完成同步。當需要退出服務器(此時已經進入同步階段)成為主服務器時,則必須等所有同步數據同步完成后才能開始服務。
如果退出服務器是因為較嚴重的故障或災難而退出的,則可能需要較長時間的處理,如更換硬件、系統(tǒng)重啟、甚至重裝系統(tǒng)等,這時就可能出現(xiàn)一些困難的情況,一種是如上圖所示的,工作正常的系統(tǒng)已經生成了新的版本,如服務器退出時的最新版本是 i,經過一段時間后,正常系統(tǒng)生成了新的版本,這時主系統(tǒng)會清空同步數據文件,重新從版本生成后進行記錄。對于這種情況,可以有兩種處理辦法:
基于本地版本的快速恢復:當退出文件服務器本地至少存在一個版本與其他正常機器上的版本相同時,可以采用這種恢復策略。具體而言,先確定一個最新的正確版本,用本地版本恢復,這一過程非常簡單快捷,僅涉及到兩次 inode 的修改;然后選擇一臺正常服務器,請求它生成一個正常系統(tǒng)上最新版本與恢復版本的增量升級數據,這樣的數據量不會很大,而且不需要象基于操作的同步那樣逐步進行,同步效率非常高,因此可以大大提高恢復速度。同步到正常系統(tǒng)的最新版本后,然后就按照上述第 2 條的情況進行同步數據文件的同步。
基于分布版本的快速恢復:當停頓時間太長而不存在一個相同的版本,或文件服務器數據出現(xiàn)損壞(如磁盤故障造成數據損毀)時,需要采用此種方法。具體辦法如下:直接把正常服務器上的最新版本傳送到退出服務器,然后按照上述的第 2 種情況進行同步數據文件的同步。
正如上面所述,全部服務器均出現(xiàn)問題的概率是很小的,但是,不能簡單的排除這種情況的出現(xiàn),特別是本方案采用數據同步機制,即多個站點的數據是保持快速同步的,這雖然能保證動態(tài)遷移的順利完成,但是也帶來較大的風險,就是會出現(xiàn)數據”污染”的自動傳播,當主文件服務器中的文件數據因為某些原因(主要是對文件的非法訪問)造成數據非法修改時,會立即傳播到其他備份節(jié)點,這樣的話,不管服務遷移到哪臺機器均會出現(xiàn)錯誤。
針對這種情況,我們采取了以下措施:當發(fā)現(xiàn)非法修改造成數據污染時,系統(tǒng)可以自動命令各站點恢復到指定的版本,如前一版本(可以由管理員配置成前一、二、三個版本);管理員也可以干預這一過程,強制各站點恢復到同一指定的版本,從而保證全局文件系統(tǒng)使用同一正確版本。
NFS 文件細粒度恢復技術
在傳統(tǒng)恢復技術中,一方面由于數據備份不是實時進行的,當出現(xiàn)事故需要恢復時,最新的備份數據與最新數據之間存在一個時間差,這樣就造成了該時間段內數據的丟失(見圖 5);同時,傳統(tǒng)的數據備份是一定時間段后數據的增量備份,是一段時間內所有文件操作疊加后的結果,因而無法精確知道在這段時間內實際數據的變化過程,因而也無法從所有這些操作中定位非法操作,并進行選擇性的恢復,以保證數據的正確性。
圖 5 因非實時備份造成恢復時的數據丟失示意圖

基于上述考慮,我們不但采用了增量方式的多版本備份恢復技術,同時還對文件的修改日志進行了實時的備份,這樣就可以在事故發(fā)生后進行基于文件操作的精確恢復,并支持允許剔除非法操作的選擇性恢復,這樣既能盡量避免因事故造成的數據丟失問題,又能通過選擇性恢復較好保證數據的正確性,同時,還可以通過對日志的分析,結合數據的精確恢復,達到發(fā)現(xiàn)犯罪線索、獲得有效證據的目的,為打擊網絡犯罪提供有力的技術手段。在這里,精確性恢復指的是恢復某一時段的所有操作,一般是在某一版本后的所有操作,不由用戶進行選擇,而選擇性恢復則指的是某一時間段內的所有操作構成的集合的子集,需要恢復的操作由用戶通過查詢、瀏覽等工具來進行選擇。在我們的定義中,實際上可以認為精確恢復為選擇性恢復的一個特例。
我們首先需要有相關的解決方法來記錄下具體的操作信息,形成操作日志文件,從而作為分析的證據(參見圖 6)。我們使用的策略是通過修改服務器操作系統(tǒng)內核調用 nfsd_write()、nfsd_create()……。從中取到調用處理對象的文件、目錄的全路徑名,寫進文件,在內核中截獲相應的文件操作請求。下面以 nfsd_rename()系統(tǒng)調用為例,進行擴充、修改而實現(xiàn)記錄操作日志的功能。
int nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname,
int flen,struct svc_fh *tfhp, char *tname, int tlen)
{
struct dentry *fdentry, *tdentry, *odentry, *ndentry;
struct inode *fdir, *tdir;
int err;
char *name;
mem_segment_t oldfs;
int fd;
err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE);
if (err)
goto out;
rqstp->rq_path1 = rqstp->rq_path2;
err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE);
if (err)
goto out;
fdentry = ffhp->fh_dentry;
fdir = fdentry->d_inode;
tdentry = tfhp->fh_dentry;
tdir = tdentry->d_inode;
……
//加入的代碼進行處理工作
if((!rqstp->rq_recover)(!S_ISDIR(odentry->d_inode->i_mode))
(odentry->d_inode->i_nlink>1)){
rqstp->rq_copy->wait = 1;
rqstp->rq_copy->done = 0;
name = get_total_name(dentry,NULL);
oldfs = get_fs();
set_fs(KERNEL_DS); //進入內核模式
fd = sys_open(/backupserv/changfilename.c,0,31681);
sys_write(fd,name,strlen(name));
sys_close(fd);
set_fs(oldfs); //從內核模式返回
評論