差异备份的一个实现--完全备份,差异备份以及恢复的实现

简介:

前面一篇文章简单阐述了备份模块的基本原理,本文要分析代码了,其实这个代码我感觉写的很烂,一来是因为时间仓促,二来是因为我根本不想写的太好,因为我们的主模块写的就不好,如果一个边角料写的太好就有点喧宾夺主的味道,公司有些人得罪不起,因此只好在vc++的.cpp文件中嵌入所谓带着.cpp后缀名的标准c程序。以下是完全备份的代码逻辑:

int FullBakup()

{

int ret = BAKUP_RESULT_OK;

RESULT result_full;

memset( &result_full,0,sizeof(RESULT) );

QueryByType( 3, &result_full, 1 ); //查询数据库,得到备份指导信息

...//判断从数据库中得到的需求的合法性

EnterCriticalSection(&g_criticalSection);

ret = FullBak( &result_full ); //进入完全备份

LeaveCriticalSection(&g_criticalSection);

return ret;

}

int FullBak(PRESULT result) //完全备份

{

BAK_FILE_INFO *g_pBakInfoHash = NULL; //内存中的备份信息,是一个数组

g_pBakInfoHash = (BAK_FILE_INFO*)malloc( 128*4096*sizeof(BAK_FILE_INFO) );

char path[MAX_PATH];char * root = "//";

strcpy_s(path,root);

clearit(g_pBakInfoHash);

strcpy(OriDir,result->strStationDir);

BakDirFiles( path, result->strStationDir,g_pBakInfoHash ); //见下面

char strCurrTime[13]; //获取当前时间戳

memset( strCurrTime, 0, 13 );

CTime tm=CTime::GetCurrentTime();

CString str;

...//将str格式化为YYYYMMDDHHMM的时间格式

strcpy_s(strCurrTime,str.GetBuffer());

int ret = ValidDir( result->strStationDir, result->strFullBakupDir );

if( ret != BAKUP_RESULT_OK)

{

...//出错处理并返回

}

ret = DisposeRar( 1, strCurrTime, result->strStationDir, result->strFullBakupDir );//产生压缩包

if( ret != BAKUP_RESULT_OK)

{

...//出错处理并返回

}

ret = WriteBakInfoToFile( strCurrTime, result->strFullBakupDir,result->strStationDir, result->strLocalIP,g_pBakInfoHash ); //保存完全备份的信息,以备差异备份或者恢复时使用,格式为首先写入当前的时间YYYYMMDDHHMM,然后依次写入BAK_FILE_INFO数组的每一条有效记录

if( ret != BAKUP_RESULT_OK )

{

...//出错处理并返回

}

return BAKUP_RESULT_OK;

}

以下函数获取备份目录的文件信息,并且导入一个BAK_FILE_INFO结构体数组中,该数组作为以下函数的输出参数:

DWORD BakDirFiles( char * dir,char *strOriDirIt,BAK_FILE_INFO *g_pBakInfoHash )

{

_WIN32_FIND_DATAA FindFileData;

HANDLE hFind = INVALID_HANDLE_VALUE;

DWORD dwError;

char curr[MAX_PATH];char currs[MAX_PATH];

memset(curr,0,MAX_PATH);memset(currs,0,MAX_PATH);

strcpy_s(curr,dir);strcpy_s(currs,strOriDirIt);

strcat_s(currs,curr);strncat_s(currs, "*", 3);

hFind = FindFirstFileA(currs, &FindFileData);

...//出错处理

else

{

while (FindNextFileA(hFind, &FindFileData) != 0)

{

if (!strcmp(FindFileData.cFileName,"..")||!strcmp(FindFileData.cFileName,"."))

{//跳过.和..

continue;

}

if ((FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) == false)

{

char temp[MAX_PATH];

memset(temp,0,MAX_PATH);

strcat_s(temp,strOriDirIt);strcat_s(temp,curr);strcat_s(temp,FindFileData.cFileName);

UINT uFileNo = GetFncode(temp); //由文件全路径得到文件编码

int i =0,iFound = 0;

unsigned int v = uFileNo%128;//计算该文件在BAK_FILE_INFO二维数组中的行位置

for( i = 0; i < 4096; i++ ) //列位置通过遍历寻找一个空闲的位置

{

PBAK_FILE_INFO bfi = g_pBakInfoHash+128*v+i;

if( bfi->used && bfi->strFileName && !strcmp(bfi->strFileName, temp) )

{//如果该文件已经存在于这个二维数组,则更新

bfi->flag = 0;

bfi->used = 1;

bfi->iLastWriteHigh = FindFileData.ftLastWriteTime.dwHighDateTime;

bfi->iLastWriteLow = FindFileData.ftLastWriteTime.dwLowDateTime;

break;

}

else if( bfi->used == 0 )

{//如果不存在,则插入新纪录

g_count++;

memset(bfi,0,sizeof(BAK_FILE_INFO));

bfi->used = 1;

bfi->flag = 0;

strcpy_s(bfi->strFileName,temp);

...//将文件访问时间和文件编码等信息存入bfi

break;

}

}

}

else if (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)

{

char temp[MAX_PATH];

memset(temp,0,MAX_PATH);

strcat_s(temp,curr);strcat_s(temp,FindFileData.cFileName);strcat(temp,"//");

BakDirFiles(temp,strOriDirIt, g_pBakInfoHash); //递归文件树

}

}

FindClose(hFind);

if (GetLastError() != ERROR_NO_MORE_FILES)

{

return BAKUP_RESULT_ERR;

}

}

return BAKUP_RESULT_OK;

}

以下是差分备份的代码逻辑:

int SubBak(PRESULT result) //差分备份

{

int ret = BAKUP_RESULT_OK;

...//以前如果没有完全备份,那么不能做差分备份,只能做完完全备份之后返回

BAK_FILE_INFO *g_pBakInfoHash = NULL;//[128][4096] = {0};

g_pBakInfoHash = (BAK_FILE_INFO*)malloc( 128*4096*sizeof(BAK_FILE_INFO) );

//建立差分备份的临时目录

strcpy( SubDir, "c://tempbak" );

CreateDirectoryA(SubDir,NULL);

char strCurrTime[13];//得到当前时间戳

memset( strCurrTime, 0, 13 );

CTime tm=CTime::GetCurrentTime();

CString str;

...//str格式化,和完全备份相同

strcpy_s(strCurrTime,str.GetBuffer());

char strLastFullTime[13];//得到最近一次的全备份时间戳

memset( strLastFullTime, 0, 13 );

ret = ReadBakInfoFromFile( strLastFullTime, result->strStationDir, result->strLocalIP,g_pBakInfoHash );

...//出错处理

char path[MAX_PATH];

char * root = "//";

strcpy_s(path,root);

int i = 0;

strcpy( OriDir, result->strStationDir );

ListFilesAndCompare(path,i,result->strStationDir,g_pBakInfoHash);//其实就是BakDirFiles的变形,为了形象,特意用一个不同的函数取代之,见下面解析

//压缩差分备份的临时目录并且存放

SearchForDeleted(g_pBakInfoHash); //找到在上次备份以来被删除的文件

ret = ValidDir( result->strStationDir,result->strSubBakupDir );

if( ret != BAKUP_RESULT_OK )

{

...//删除临时目录,处理错误,释放资源并返回

}

ret = DisposeRar( 1, strCurrTime, SubDir, result->strSubBakupDir );

if( ret != BAKUP_RESULT_OK)

{

...//删除临时目录,处理错误,释放资源并返回

}

//将信息存入数据库的bakup_info表

ret = WriteInfoToDatabase( strLastFullTime, result->strFullBakupDir,strCurrTime, result->strSubBakupDir, result->strStationDir,result->strLocalIP,g_pBakInfoHash );

if( ret != BAKUP_RESULT_OK )

{

...//处理错误,释放资源并返回

}

RemoveSubDir( SubDir ); //删除差分备份的临时目录

...//处理错误,释放资源并返回

}

DWORD ListFilesAndCompare( char * dir, int & iDelete, char * strOriDirIt,BAK_FILE_INFO *g_pBakInfoHash )

{

int iNewDir = 0;

...//和BakDirFiles相同,直到下面的判断,以下省略代码前面的缩进,实际上应该在一个缩进很长的for循环里面

if ((FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) == false)

{

char temp[MAX_PATH];

memset(temp,0,MAX_PATH);

strcat_s(temp,strOriDirIt);strcat_s(temp,curr);strcat_s(temp,FindFileData.cFileName);

UINT uFileNo = GetFncode(temp);

int i =0,iFound = 0;

unsigned int v = uFileNo%128;

for( i = 0; i < 4096; i++ )

{

BAK_FILE_INFO bfi = *(g_pBakInfoHash+128*v+i);

if( !bfi.flag && !strcmp(bfi.strFileName, temp) && bfi.iFileNo == uFileNo &&

bfi.iLastWriteHigh == FindFileData.ftLastWriteTime.dwHighDateTime &&

bfi.iLastWriteLow == FindFileData.ftLastWriteTime.dwLowDateTime )

{ //找到完全一样的文件,那么就不需要备份了,将iFound设置为1

((PBAK_FILE_INFO)(g_pBakInfoHash+128*v+i))->flag = 1;

iFound = 1;

break; //已经找到就不需要继续遍历同一行的相同文件编码的文件了

}

else if(!bfi.flag && !strcmp(bfi.strFileName, temp) && bfi.iFileNo == uFileNo)

{ //文件名称一样,并且flag为0,说明没有被访问过,现在找到了,将flag设置为1,说明已经访问过了,但是最后访问时间变了,因此不将iFound设置为1,这就是说要备份该文件

((PBAK_FILE_INFO)(g_pBakInfoHash+128*v+i))->flag = 1;

}

}

if( iFound )

continue;

else //备份的过程

{

iDelete = 1; //将iDelete设置为1,说明该目录有文件被备份了,在上层目录不要删除了它,否则就需要删除这个目录,因为该备份程序不备份空目录,注意这个iDelete是递归设置的,只要有一个子目录中有文件被备份就不删除它。

char tempsubpath[MAX_PATH];

memset(tempsubpath,0,MAX_PATH);

strcpy_s(tempsubpath,SubDir);strcat_s(tempsubpath,curr);

strcat_s(tempsubpath,FindFileData.cFileName);

CopyFileA(temp,tempsubpath,false);

}

}

else if (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)

{

char tempsubpath[MAX_PATH];

memset(tempsubpath,0,MAX_PATH);

strcpy_s(tempsubpath,SubDir);strcat_s(tempsubpath,curr);

strcat_s(tempsubpath,FindFileData.cFileName);

//在差分备份的临时目录中实时建立新目录

if(CreateDirectoryA(tempsubpath,NULL))

{

iNewDir = 1;

}

char temp[MAX_PATH];

memset(temp,0,MAX_PATH);

strcat_s(temp,curr);strcat_s(temp,FindFileData.cFileName);

iDelete = 0;

strcat(temp,"//");

ListFilesAndCompare(temp,iDelete,strOriDirIt,g_pBakInfoHash);

if( !iDelete && iNewDir ) //如果子目录是空的,说明子目录没有文件需要备份,删除新建目录

RemoveDirectoryA( tempsubpath );

}

...//返回操作和BakDirFiles相同

}

以下的函数就是搜集被删除文件的:

void SearchForDeleted(BAK_FILE_INFO* g_pBakInfoHash)

{

char temp[MAX_PATH];

memset(temp,0,MAX_PATH);

strcat_s(temp,SubDir);strcat_s(temp,"//");strcat_s(temp,"1qaz2wsx0okm");

FILE * fp ;

errno_t errn = fopen_s(&fp,temp,"wc");

...//出错处理

int i,j;

for( i = 0; i<128; i++ )

for( j = 0; j<4096; j++ )

{

PBAK_FILE_INFO bfi = (g_pBakInfoHash+128*i+j);

if( bfi->flag )

continue;

else if(bfi->used) //寻找所有有效bfi中的flag为0的bfi,将之写入文件。

{

fwrite( bfi->strFileName, bfi->iFileNameLen, 1, fp );

fwrite( "/r/n", 2, 1, fp );

bfi->used = 0;

}

}

fclose(fp);

}

以下是备份恢复的逻辑,注意,备份恢复是按照YYYYMMDDHHMM时间来恢复的,并不区分是完全备份还是差分备份:

int Recover() //恢复

{

int ret = BAKUP_RESULT_OK;

char strResult[13];

RESULT result;

memset( strResult, 0, 13 );memset( &result, 0, sizeof(RESULT) );

QueryByType( 5, &result, 1 );

...//验证指导信息的合法性

strcpy( result.strRecoverSub, result.strTimeTo );

...

if(... && strcmp(result.strRecoverFull,"") && strcmp( result.strRecoverFull,result.strTimeTo ) )

{//如果完全备份的时间和差分备份的时间不同,则说明需要首先恢复完全备份

if(!DisposeRar( 0, result.strRecoverFull,result.strRecoverDir,result.strFullBakupDir ))

...//出错以及善后处理

}

...//以下无论如何都要恢复差分备份

ret = DisposeRar( 0, result.strRecoverSub,result.strRecoverDir,result.strSubBakupDir );

...//出错以及善后处理

DeleteLS( result.strRecoverDir, result.strStationDir ); //删除需要删除的文件,依据就是差分备份时搜集到的需要删除的文件,可以随意存放,我是将其存放在差分备份的压缩包里面了

return BAKUP_RESULT_OK;

}

完毕,代码逻辑介绍到此结束,原本这个模块中还有自动备份设置,这里就不多说了,记得我曾经写过一篇关于应用程序timer的文章,就是这个备份模块中自动备份相关的,这里就不说了,无非就是单独开一个线程,然后设置几个timer即可。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273646

相关文章
|
9月前
完全备份、差异备份以及增量备份的区别
完全备份、差异备份以及增量备份的区别
257 0
|
存储 安全 容灾
备份方式
备份方式
159 0
|
存储 容灾 关系型数据库
7 PostgreSQL 物理备份和还原,逻辑备份和还原|学习笔记
快速学习7 PostgreSQL 物理备份和还原,逻辑备份和还原
982 0
7 PostgreSQL 物理备份和还原,逻辑备份和还原|学习笔记
|
存储 SQL Cloud Native
用户指南—备份与恢复—备份数据
PolarDB-X支持自动备份及手动备份,方便您恢复历史数据。 本文介绍数据备份的相关功能。
127 0
用户指南—备份与恢复—备份数据