mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
2291 lines
53 KiB
C++
2291 lines
53 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include "client.h"
|
|
#include "../Interface/Server.h"
|
|
#include "../Interface/File.h"
|
|
#include "../Interface/SettingsReader.h"
|
|
#ifdef _WIN32
|
|
#include "DirectoryWatcherThread.h"
|
|
#else
|
|
#include <errno.h>
|
|
#endif
|
|
#include "../stringtools.h"
|
|
#include "../common/data.h"
|
|
#include "database.h"
|
|
#include "ServerIdentityMgr.h"
|
|
#include "ClientService.h"
|
|
#include "../urbackupcommon/sha2/sha2.h"
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <stdlib.h>
|
|
|
|
//For truncating files
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <sys\stat.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#endif
|
|
volatile bool IdleCheckerThread::idle=false;
|
|
volatile bool IdleCheckerThread::pause=false;
|
|
volatile bool IndexThread::stop_index=false;
|
|
std::map<std::wstring, std::wstring> IndexThread::filesrv_share_dirs;
|
|
|
|
const char IndexThread::IndexThreadAction_GetLog=9;
|
|
|
|
extern PLUGIN_ID filesrv_pluginid;
|
|
|
|
const unsigned int idletime=60000;
|
|
const unsigned int nonidlesleeptime=500;
|
|
const unsigned short tcpport=35621;
|
|
const unsigned short udpport=35622;
|
|
const unsigned int shadowcopy_timeout=7*24*60*60*1000;
|
|
const size_t max_modify_file_buffer_size=500*1024;
|
|
const size_t max_modify_hash_buffer_size=500*1024;
|
|
const int64 save_filehash_limit=20*4096;
|
|
|
|
#ifndef SERVER_ONLY
|
|
#define ENABLE_VSS
|
|
#endif
|
|
|
|
#define CHECK_COM_RESULT_RELEASE(x) { HRESULT r; if( (r=(x))!=S_OK ){ VSSLog(#x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); if(backupcom!=NULL){backupcom->AbortBackup();backupcom->Release();} return false; }}
|
|
#define CHECK_COM_RESULT_RETURN(x) { HRESULT r; if( (r=(x))!=S_OK ){ VSSLog( #x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); return false; }}
|
|
#define CHECK_COM_RESULT_RELEASE_S(x) { HRESULT r; if( (r=(x))!=S_OK ){ VSSLog( #x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); if(backupcom!=NULL){backupcom->AbortBackup();backupcom->Release();} return ""; }}
|
|
#define CHECK_COM_RESULT(x) { HRESULT r; if( (r=(x))!=S_OK ){ VSSLog( #x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); }}
|
|
#define CHECK_COM_RESULT_OK(x, ok) { HRESULT r; if( (r=(x))!=S_OK ){ ok=false; VSSLog( #x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); }}
|
|
#define CHECK_COM_RESULT_OK_HR(x, ok, r) { if( (r=(x))!=S_OK ){ ok=false; VSSLog( #x+(std::string)" failed: EC="+GetErrorHResErrStr(r), LL_ERROR); }}
|
|
|
|
void IdleCheckerThread::operator()(void)
|
|
{
|
|
int lx,ly;
|
|
int x,y;
|
|
getMousePos(x,y);
|
|
lx=x;
|
|
ly=y;
|
|
|
|
unsigned int last_move=Server->getTimeMS();
|
|
|
|
while(true)
|
|
{
|
|
Server->wait(1000);
|
|
getMousePos(x,y);
|
|
if(x!=lx || y!=ly )
|
|
{
|
|
lx=x;
|
|
ly=y;
|
|
last_move=Server->getTimeMS();
|
|
idle=false;
|
|
}
|
|
else if(Server->getTimeMS()-last_move>idletime)
|
|
{
|
|
idle=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IdleCheckerThread::getIdle(void)
|
|
{
|
|
return true;//idle;
|
|
}
|
|
|
|
bool IdleCheckerThread::getPause(void)
|
|
{
|
|
return pause;
|
|
}
|
|
|
|
void IdleCheckerThread::setPause(bool b)
|
|
{
|
|
pause=b;
|
|
}
|
|
|
|
IMutex *IndexThread::filelist_mutex=NULL;
|
|
IPipe* IndexThread::msgpipe=NULL;
|
|
IFileServ *IndexThread::filesrv=NULL;
|
|
IMutex *IndexThread::filesrv_mutex=NULL;
|
|
|
|
IndexThread::IndexThread(void)
|
|
{
|
|
if(filelist_mutex==NULL)
|
|
filelist_mutex=Server->createMutex();
|
|
if(msgpipe==NULL)
|
|
msgpipe=Server->createMemoryPipe();
|
|
if(filesrv_mutex==NULL)
|
|
filesrv_mutex=Server->createMutex();
|
|
|
|
contractor=NULL;
|
|
|
|
dwt=NULL;
|
|
|
|
if(Server->getPlugin(Server->getThreadID(), filesrv_pluginid))
|
|
{
|
|
start_filesrv();
|
|
}
|
|
else
|
|
{
|
|
filesrv=NULL;
|
|
Server->Log("Error starting fileserver", LL_ERROR);
|
|
}
|
|
|
|
modify_file_buffer_size=0;
|
|
end_to_end_file_backup_verification_enabled=0;
|
|
calculate_filehashes_on_client=0;
|
|
last_tmp_update_time=0;
|
|
}
|
|
|
|
IndexThread::~IndexThread()
|
|
{
|
|
filesrv->stopServer();
|
|
|
|
#ifdef _WIN32
|
|
if(dwt!=NULL)
|
|
{
|
|
dwt->stop();
|
|
Server->getThreadPool()->waitFor(dwt_ticket);
|
|
delete dwt;
|
|
}
|
|
#endif
|
|
|
|
((IFileServFactory*)(Server->getPlugin(Server->getThreadID(), filesrv_pluginid)))->destroyFileServ(filesrv);
|
|
Server->destroy(filelist_mutex);
|
|
Server->destroy(msgpipe);
|
|
Server->destroy(filesrv_mutex);
|
|
cd->destroyQueries();
|
|
delete cd;
|
|
}
|
|
|
|
IMutex* IndexThread::getFilelistMutex(void)
|
|
{
|
|
return filelist_mutex;
|
|
}
|
|
|
|
void IndexThread::updateDirs(void)
|
|
{
|
|
readBackupDirs();
|
|
|
|
#ifdef _WIN32
|
|
std::vector<std::wstring> watching;
|
|
for(size_t i=0;i<backup_dirs.size();++i)
|
|
{
|
|
watching.push_back(backup_dirs[i].path);
|
|
}
|
|
if(dwt==NULL)
|
|
{
|
|
dwt=new DirectoryWatcherThread(watching);
|
|
dwt_ticket=Server->getThreadPool()->execute(dwt);
|
|
}
|
|
else
|
|
{
|
|
for(size_t i=0;i<backup_dirs.size();++i)
|
|
{
|
|
std::wstring msg=L"A"+backup_dirs[i].path;
|
|
dwt->getPipe()->Write((char*)msg.c_str(), sizeof(wchar_t)*msg.size());
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void IndexThread::operator()(void)
|
|
{
|
|
Server->waitForStartupComplete();
|
|
|
|
#ifdef _WIN32
|
|
#ifndef SERVER_ONLY
|
|
CHECK_COM_RESULT(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
|
|
CHECK_COM_RESULT(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL));
|
|
#endif
|
|
#endif
|
|
#ifdef _WIN32
|
|
|
|
#ifdef THREAD_MODE_BACKGROUND_BEGIN
|
|
#if defined(VSS_XP) || defined(VSS_S03)
|
|
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_LOWEST);
|
|
#else
|
|
SetThreadPriority( GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
|
|
#endif
|
|
#else
|
|
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_LOWEST);
|
|
#endif //THREAD_MODE_BACKGROUND_BEGIN
|
|
|
|
#endif
|
|
|
|
db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
|
|
db->Write("CREATE TEMPORARY TABLE files_tmp (num NUMERIC, data BLOB, name TEXT);");
|
|
db->Write("CREATE TEMPORARY TABLE filehashes_tmp (name TEXT, filesize INTEGER, modifytime INTEGER, hashdata BLOB);");
|
|
|
|
cd=new ClientDAO(Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT));
|
|
|
|
#ifdef _WIN32
|
|
#ifdef ENABLE_VSS
|
|
cleanup_saved_shadowcopies();
|
|
#endif
|
|
#endif
|
|
|
|
//indexDirs();
|
|
|
|
updateDirs();
|
|
|
|
while(true)
|
|
{
|
|
std::string msg;
|
|
if(contractor!=NULL)
|
|
{
|
|
while(msg!="exit")
|
|
{
|
|
contractor->Read(&msg);
|
|
if(msg!="exit")
|
|
{
|
|
contractor->Write(msg);
|
|
Server->wait(100);
|
|
}
|
|
}
|
|
Server->destroy(contractor);
|
|
}
|
|
msgpipe->Read(&msg);
|
|
|
|
CRData data(&msg);
|
|
char action;
|
|
data.getChar(&action);
|
|
data.getVoidPtr((void**)&contractor);
|
|
if(action==0)
|
|
{
|
|
vsslog.clear();
|
|
|
|
data.getStr(&starttoken);
|
|
data.getInt(&end_to_end_file_backup_verification_enabled);
|
|
data.getInt(&calculate_filehashes_on_client);
|
|
|
|
//incr backup
|
|
readBackupDirs();
|
|
if(backup_dirs.empty())
|
|
{
|
|
contractor->Write("no backup dirs");
|
|
continue;
|
|
}
|
|
#ifdef _WIN32
|
|
if(cd->hasChangedGap())
|
|
{
|
|
Server->Log("Deleting file-index... GAP found...", LL_INFO);
|
|
|
|
std::vector<std::wstring> gaps=cd->getGapDirs();
|
|
|
|
std::string q_str="DELETE FROM files";
|
|
if(!gaps.empty())
|
|
{
|
|
q_str+=" WHERE ";
|
|
}
|
|
for(size_t i=0;i<gaps.size();++i)
|
|
{
|
|
q_str+="name GLOB ?";
|
|
if(i+1<gaps.size())
|
|
q_str+=" OR ";
|
|
}
|
|
|
|
IQuery *q=db->Prepare(q_str, false);
|
|
for(size_t i=0;i<gaps.size();++i)
|
|
{
|
|
Server->Log(L"Deleting file-index from drive \""+gaps[i]+L"\"", LL_INFO);
|
|
q->Bind(gaps[i]+L"*");
|
|
}
|
|
|
|
q->Write();
|
|
q->Reset();
|
|
db->destroyQuery(q);
|
|
|
|
if(dwt!=NULL)
|
|
{
|
|
dwt->stop();
|
|
Server->getThreadPool()->waitFor(dwt_ticket);
|
|
delete dwt;
|
|
dwt=NULL;
|
|
updateDirs();
|
|
}
|
|
}
|
|
#endif
|
|
execute_prebackup_hook();
|
|
if(!stop_index)
|
|
{
|
|
indexDirs();
|
|
if(!stop_index)
|
|
{
|
|
contractor->Write("done");
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("error - stop_index 2");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("error - stop_index 1");
|
|
}
|
|
execute_postindex_hook();
|
|
}
|
|
else if(action==1)
|
|
{
|
|
vsslog.clear();
|
|
|
|
data.getStr(&starttoken);
|
|
data.getInt(&end_to_end_file_backup_verification_enabled);
|
|
data.getInt(&calculate_filehashes_on_client);
|
|
|
|
readBackupDirs();
|
|
if(backup_dirs.empty())
|
|
{
|
|
contractor->Write("no backup dirs");
|
|
continue;
|
|
}
|
|
//full backup
|
|
{
|
|
cd->deleteChangedDirs();
|
|
cd->deleteSavedChangedDirs();
|
|
|
|
Server->Log("Deleting files... doing full index...", LL_INFO);
|
|
db->Write("DELETE FROM files");
|
|
db->Write("DELETE FROM filehashes");
|
|
}
|
|
execute_prebackup_hook();
|
|
indexDirs();
|
|
execute_postindex_hook();
|
|
if(!stop_index)
|
|
{
|
|
contractor->Write("done");
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("error");
|
|
}
|
|
}
|
|
else if(action==2) // create shadowcopy
|
|
{
|
|
std::string scdir;
|
|
data.getStr(&scdir);
|
|
data.getStr(&starttoken);
|
|
uchar image_backup=0;
|
|
data.getUChar(&image_backup);
|
|
std::wstring wscdir=Server->ConvertToUnicode(scdir);
|
|
SCDirs *scd=getSCDir(wscdir);
|
|
|
|
unsigned char fileserv;
|
|
bool hfs=data.getUChar(&fileserv);
|
|
|
|
if(scd->running==true && Server->getTimeSeconds()-scd->starttime<shadowcopy_timeout/1000)
|
|
{
|
|
if(scd->ref!=NULL && image_backup==0)
|
|
{
|
|
scd->ref->dontincrement=true;
|
|
}
|
|
if(start_shadowcopy(scd, NULL, image_backup==1?true:false, std::vector<SCRef*>(), image_backup==1?true:false))
|
|
{
|
|
contractor->Write("done-"+nconvert(scd->ref->save_id)+"-"+Server->ConvertToUTF8(scd->target));
|
|
}
|
|
else
|
|
{
|
|
VSSLog(L"Getting shadowcopy of \""+scd->dir+L"\" failed.", LL_ERROR);
|
|
contractor->Write("failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(scd->running==true)
|
|
{
|
|
Server->Log(L"Removing shadowcopy \""+scd->dir+L"\" because of timeout...", LL_WARNING);
|
|
bool b=release_shadowcopy(scd, false, -1, scd);
|
|
if(!b)
|
|
{
|
|
#ifdef _WIN32
|
|
Server->Log(L"Deleting shadowcopy of \""+scd->dir+L"\" failed.", LL_ERROR);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
scd->dir=wscdir;
|
|
scd->starttime=Server->getTimeSeconds();
|
|
if(hfs && fileserv==0)
|
|
{
|
|
scd->target=scd->dir;
|
|
scd->fileserv=false;
|
|
}
|
|
else
|
|
{
|
|
scd->target=getShareDir(scd->dir);
|
|
scd->fileserv=true;
|
|
}
|
|
|
|
scd->orig_target=scd->target;
|
|
|
|
Server->Log(L"Creating shadowcopy of \""+scd->dir+L"\"...", LL_DEBUG);
|
|
bool b=start_shadowcopy(scd, NULL, image_backup==1?true:false, std::vector<SCRef*>(), image_backup==0?false:true);
|
|
Server->Log("done.", LL_DEBUG);
|
|
if(!b || scd->ref==NULL)
|
|
{
|
|
contractor->Write("failed");
|
|
#ifdef _WIN32
|
|
Server->Log(L"Creating shadowcopy of \""+scd->dir+L"\" failed.", LL_ERROR);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("done-"+nconvert(scd->ref->save_id)+"-"+Server->ConvertToUTF8(scd->target));
|
|
scd->running=true;
|
|
}
|
|
}
|
|
}
|
|
else if(action==3) // remove shadowcopy
|
|
{
|
|
std::string scdir;
|
|
data.getStr(&scdir);
|
|
data.getStr(&starttoken);
|
|
uchar image_backup=0;
|
|
data.getUChar(&image_backup);
|
|
SCDirs *scd=getSCDir(Server->ConvertToUnicode(scdir));
|
|
|
|
int save_id=-1;
|
|
data.getInt(&save_id);
|
|
|
|
if(scd->running==false )
|
|
{
|
|
if(!release_shadowcopy(scd, image_backup==1?true:false, save_id))
|
|
{
|
|
Server->Log("Invalid action -- Creating shadow copy failed?", LL_ERROR);
|
|
contractor->Write("failed");
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("done");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::wstring release_dir=scd->dir;
|
|
bool b=release_shadowcopy(scd, image_backup==1?true:false, save_id);
|
|
if(!b)
|
|
{
|
|
contractor->Write("failed");
|
|
#ifdef _WIN32
|
|
Server->Log(L"Deleting shadowcopy of \""+release_dir+L"\" failed.", LL_ERROR);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("done");
|
|
}
|
|
}
|
|
}
|
|
else if(action==4) //lookup shadowdrive path
|
|
{
|
|
int save_id;
|
|
if(data.getInt(&save_id))
|
|
{
|
|
std::string path=lookup_shadowcopy(save_id);
|
|
if(path.empty())
|
|
{
|
|
contractor->Write("failed");
|
|
}
|
|
else
|
|
{
|
|
contractor->Write("done-"+nconvert(save_id)+"-"+path);
|
|
}
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
else if(action==5) //add watch directory
|
|
{
|
|
std::string dir;
|
|
if(data.getStr(&dir))
|
|
{
|
|
std::wstring msg=L"A"+os_get_final_path(Server->ConvertToUnicode( dir ));
|
|
dwt->getPipe()->Write((char*)msg.c_str(), sizeof(wchar_t)*msg.size());
|
|
}
|
|
contractor->Write("done");
|
|
stop_index=false;
|
|
}
|
|
else if(action==6) //remove watch directory
|
|
{
|
|
std::string dir;
|
|
if(data.getStr(&dir))
|
|
{
|
|
std::wstring msg=L"D"+os_get_final_path(Server->ConvertToUnicode( dir ));
|
|
dwt->getPipe()->Write((char*)msg.c_str(), sizeof(wchar_t)*msg.size());
|
|
}
|
|
contractor->Write("done");
|
|
stop_index=false;
|
|
}
|
|
#endif
|
|
else if(action==7) // restart filesrv
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
filesrv->stopServer();
|
|
start_filesrv();
|
|
readBackupDirs();
|
|
}
|
|
else if(action==8) //stop
|
|
{
|
|
break;
|
|
}
|
|
else if(action==IndexThreadAction_GetLog)
|
|
{
|
|
std::string ret;
|
|
for(size_t i=0;i<vsslog.size();++i)
|
|
{
|
|
ret+=nconvert(vsslog[i].second)+"-"+vsslog[i].first+"\n";
|
|
}
|
|
contractor->Write(ret);
|
|
}
|
|
}
|
|
|
|
delete this;
|
|
}
|
|
|
|
const char * filelist_fn="urbackup/data/filelist_new.ub";
|
|
|
|
void IndexThread::indexDirs(void)
|
|
{
|
|
bool patterns_changed=false;
|
|
readPatterns(patterns_changed, false);
|
|
|
|
if(patterns_changed)
|
|
{
|
|
VSSLog("Deleting file-cache because include/exclude pattern changed...", LL_INFO);
|
|
db->Write("DELETE FROM files");
|
|
}
|
|
|
|
updateDirs();
|
|
#ifdef _WIN32
|
|
cd->restoreSavedChangedDirs();
|
|
cd->restoreSavedChangedFiles();
|
|
//Invalidate cache
|
|
DirectoryWatcherThread::update_and_wait();
|
|
changed_dirs=cd->getChangedDirs();
|
|
cd->moveChangedFiles();
|
|
|
|
bool has_stale_shadowcopy=false;
|
|
|
|
cd->restoreSavedDelDirs();
|
|
std::vector<std::wstring> deldirs=cd->getDelDirs();
|
|
for(size_t i=0;i<deldirs.size();++i)
|
|
{
|
|
cd->removeDeletedDir(deldirs[i]);
|
|
}
|
|
#endif
|
|
std::sort(changed_dirs.begin(), changed_dirs.end());
|
|
|
|
std::vector<SCRef*> past_refs;
|
|
|
|
last_tmp_update_time=Server->getTimeMS();
|
|
|
|
{
|
|
std::fstream outfile(filelist_fn, std::ios::out|std::ios::binary);
|
|
for(size_t i=0;i<backup_dirs.size();++i)
|
|
{
|
|
SCDirs *scd=getSCDir(backup_dirs[i].tname);
|
|
if(!scd->running)
|
|
{
|
|
scd->dir=backup_dirs[i].tname;
|
|
scd->starttime=Server->getTimeSeconds();
|
|
scd->target=getShareDir(backup_dirs[i].tname);
|
|
scd->orig_target=scd->target;
|
|
}
|
|
scd->fileserv=true;
|
|
|
|
std::wstring mod_path=backup_dirs[i].path;
|
|
|
|
VSSLog(L"Creating shadowcopy of \""+scd->dir+L"\" in indexDirs()", LL_DEBUG);
|
|
bool onlyref=true;
|
|
bool stale_shadowcopy=false;
|
|
bool b=start_shadowcopy(scd, &onlyref, true, past_refs, false, &stale_shadowcopy);
|
|
VSSLog("done.", LL_DEBUG);
|
|
|
|
#ifdef _WIN32
|
|
if(stale_shadowcopy)
|
|
{
|
|
has_stale_shadowcopy=true;
|
|
}
|
|
#endif
|
|
|
|
if(!b)
|
|
{
|
|
#ifdef _WIN32
|
|
VSSLog(L"Creating shadowcopy of \""+scd->dir+L"\" failed in indexDirs().", LL_ERROR);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
mod_path=scd->target;
|
|
scd->running=true;
|
|
}
|
|
#ifdef _WIN32
|
|
if(!b || !onlyref)
|
|
{
|
|
past_refs.push_back(scd->ref);
|
|
DirectoryWatcherThread::update_and_wait();
|
|
Server->wait(1000);
|
|
std::vector<SMDir> acd=cd->getChangedDirs(false);
|
|
changed_dirs.insert(changed_dirs.end(), acd.begin(), acd.end() );
|
|
std::sort(changed_dirs.begin(), changed_dirs.end());
|
|
cd->moveChangedFiles(false);
|
|
|
|
std::vector<std::wstring> deldirs=cd->getDelDirs(false);
|
|
for(size_t i=0;i<deldirs.size();++i)
|
|
{
|
|
cd->removeDeletedDir(deldirs[i]);
|
|
}
|
|
}
|
|
#endif
|
|
mod_path=removeDirectorySeparatorAtEnd(mod_path);
|
|
backup_dirs[i].path=removeDirectorySeparatorAtEnd(backup_dirs[i].path);
|
|
|
|
VSSLog(L"Indexing \""+backup_dirs[i].tname+L"\"...", LL_DEBUG);
|
|
index_c_db=0;
|
|
index_c_fs=0;
|
|
index_c_db_update=0;
|
|
outfile << "d\"" << Server->ConvertToUTF8(backup_dirs[i].tname) << "\"\n";
|
|
//db->Write("BEGIN IMMEDIATE;");
|
|
last_transaction_start=Server->getTimeMS();
|
|
initialCheck( backup_dirs[i].path, mod_path, backup_dirs[i].tname, outfile, true);
|
|
|
|
cd->copyFromTmpFiles();
|
|
cd->copyFromTmpFileHashes();
|
|
cd->deleteTmpFileHashes();
|
|
commitModifyFilesBuffer();
|
|
commitModifyHashBuffer();
|
|
|
|
if(stop_index)
|
|
{
|
|
for(size_t k=0;k<backup_dirs.size();++k)
|
|
{
|
|
SCDirs *scd=getSCDir(backup_dirs[k].tname);
|
|
release_shadowcopy(scd);
|
|
}
|
|
|
|
outfile.close();
|
|
removeFile(Server->ConvertToUnicode(filelist_fn));
|
|
return;
|
|
}
|
|
//db->EndTransaction();
|
|
outfile << "d\"..\"\n";
|
|
VSSLog(L"Indexing of \""+backup_dirs[i].tname+L"\" done. "+convert(index_c_fs)+L" filesystem lookups "+convert(index_c_db)+L" db lookups and "+convert(index_c_db_update)+L" db updates" , LL_INFO);
|
|
}
|
|
std::streampos pos=outfile.tellp();
|
|
outfile.seekg(0, std::ios::end);
|
|
if(pos!=outfile.tellg())
|
|
{
|
|
outfile.close();
|
|
bool b=os_file_truncate(Server->ConvertToUnicode(filelist_fn), pos);
|
|
if(!b)
|
|
{
|
|
VSSLog("Error changing filelist size", LL_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
cd->copyFromTmpFiles();
|
|
cd->copyFromTmpFileHashes();
|
|
cd->deleteTmpFileHashes();
|
|
commitModifyFilesBuffer();
|
|
commitModifyHashBuffer();
|
|
|
|
#ifdef _WIN32
|
|
if(!has_stale_shadowcopy)
|
|
{
|
|
VSSLog("Deleting backup of changed dirs...", LL_DEBUG);
|
|
cd->deleteSavedChangedDirs();
|
|
cd->deleteSavedDelDirs();
|
|
cd->deleteSavedChangedFiles();
|
|
}
|
|
else
|
|
{
|
|
VSSLog("Did not delete backup of changed dirs because a stale shadowcopy was used.", LL_INFO);
|
|
}
|
|
#endif
|
|
|
|
{
|
|
IScopedLock lock(filelist_mutex);
|
|
removeFile(L"urbackup/data/filelist.ub");
|
|
moveFile(L"urbackup/data/filelist_new.ub", L"urbackup/data/filelist.ub");
|
|
Server->wait(1000);
|
|
}
|
|
|
|
if(patterns_changed)
|
|
{
|
|
readPatterns(patterns_changed, true);
|
|
}
|
|
share_dirs(starttoken);
|
|
}
|
|
|
|
bool IndexThread::initialCheck(const std::wstring &orig_dir, const std::wstring &dir, const std::wstring &named_path, std::fstream &outfile, bool first)
|
|
{
|
|
bool has_include=false;
|
|
|
|
//Server->Log(L"Indexing "+dir, LL_DEBUG);
|
|
if(Server->getTimeMS()-last_transaction_start>1000)
|
|
{
|
|
/*db->EndTransaction();
|
|
db->Write("BEGIN IMMEDIATE;");*/
|
|
last_transaction_start=Server->getTimeMS();
|
|
}
|
|
if( IdleCheckerThread::getIdle()==false )
|
|
{
|
|
Server->wait(nonidlesleeptime);
|
|
}
|
|
if(IdleCheckerThread::getPause())
|
|
{
|
|
Server->wait(5000);
|
|
}
|
|
|
|
if(stop_index)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<SFile> files=getFilesProxy(orig_dir, dir, !first);
|
|
|
|
for(size_t i=0;i<files.size();++i)
|
|
{
|
|
if( !files[i].isdir )
|
|
{
|
|
if( isExcluded(orig_dir+os_file_sep()+files[i].name) || isExcluded(named_path+os_file_sep()+files[i].name) )
|
|
{
|
|
continue;
|
|
}
|
|
if( !isIncluded(orig_dir+os_file_sep()+files[i].name, NULL) && !isIncluded(named_path+os_file_sep()+files[i].name, NULL) )
|
|
{
|
|
continue;
|
|
}
|
|
has_include=true;
|
|
outfile << "f\"" << Server->ConvertToUTF8(files[i].name) << "\" " << files[i].size << " " << files[i].last_modified;
|
|
|
|
if(end_to_end_file_backup_verification_enabled || calculate_filehashes_on_client)
|
|
{
|
|
outfile << "#";
|
|
|
|
if(calculate_filehashes_on_client)
|
|
{
|
|
std::string hash;
|
|
if(files[i].size<=save_filehash_limit)
|
|
{
|
|
hash=getSHA512Binary(dir+os_file_sep()+files[i].name);
|
|
}
|
|
else
|
|
{
|
|
std::wstring key_path=orig_dir+os_file_sep()+files[i].name;
|
|
|
|
_i64 filesize;
|
|
_i64 modifytime;
|
|
if(cd->getFileHash(key_path, filesize, modifytime, hash))
|
|
{
|
|
if( filesize!=files[i].size &&
|
|
files[i].last_modified!=modifytime )
|
|
{
|
|
hash=getSHA512Binary(dir+os_file_sep()+files[i].name);
|
|
|
|
modifyHashInt(hash, key_path, files[i].size, files[i].last_modified);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hash=getSHA512Binary(dir+os_file_sep()+files[i].name);
|
|
cd->addFileHash(key_path, files[i].size, files[i].last_modified, hash);
|
|
if(Server->getTimeMS()-last_tmp_update_time>10*60*1000) //10min
|
|
{
|
|
cd->copyFromTmpFileHashes();
|
|
cd->deleteTmpFileHashes();
|
|
last_tmp_update_time=Server->getTimeMS();
|
|
}
|
|
}
|
|
}
|
|
|
|
outfile << "sha512=" << base64_encode(reinterpret_cast<const unsigned char*>(hash.c_str()), static_cast<unsigned int>(hash.size()));
|
|
}
|
|
|
|
if(end_to_end_file_backup_verification_enabled)
|
|
{
|
|
if(calculate_filehashes_on_client) outfile << "&";
|
|
|
|
outfile << "sha256=" << getSHA256(dir+os_file_sep()+files[i].name);
|
|
}
|
|
}
|
|
|
|
outfile << "\n";
|
|
}
|
|
}
|
|
|
|
for(size_t i=0;i<files.size();++i)
|
|
{
|
|
if( files[i].isdir )
|
|
{
|
|
if( isExcluded(orig_dir+os_file_sep()+files[i].name) || isExcluded(named_path+os_file_sep()+files[i].name) )
|
|
{
|
|
continue;
|
|
}
|
|
bool curr_included=false;
|
|
bool adding_worthless1, adding_worthless2;
|
|
if( isIncluded(orig_dir+os_file_sep()+files[i].name, &adding_worthless1) || isIncluded(named_path+os_file_sep()+files[i].name, &adding_worthless2) )
|
|
{
|
|
has_include=true;
|
|
curr_included=true;
|
|
}
|
|
|
|
if( curr_included || !adding_worthless1 || !adding_worthless2 )
|
|
{
|
|
std::streampos pos=outfile.tellp();
|
|
outfile << "d\"" << Server->ConvertToUTF8(files[i].name) << "\"\n";
|
|
bool b=initialCheck(orig_dir+os_file_sep()+files[i].name, dir+os_file_sep()+files[i].name, named_path+os_file_sep()+files[i].name, outfile);
|
|
outfile << "d\"..\"\n";
|
|
|
|
if(!b)
|
|
{
|
|
if(!curr_included)
|
|
{
|
|
outfile.seekp(pos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
has_include=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return has_include;
|
|
}
|
|
|
|
void IndexThread::readBackupDirs(void)
|
|
{
|
|
backup_dirs=cd->getBackupDirs();
|
|
|
|
for(size_t i=0;i<backup_dirs.size();++i)
|
|
{
|
|
#ifdef _WIN32
|
|
backup_dirs[i].path=os_get_final_path(backup_dirs[i].path);
|
|
Server->Log(L"Final path: "+backup_dirs[i].path, LL_INFO);
|
|
#endif
|
|
|
|
if(filesrv!=NULL)
|
|
shareDir(backup_dirs[i].tname, backup_dirs[i].path);
|
|
}
|
|
}
|
|
|
|
std::vector<SFile> IndexThread::getFilesProxy(const std::wstring &orig_path, const std::wstring &path, bool use_db)
|
|
{
|
|
#ifndef _WIN32
|
|
if(path.empty())
|
|
{
|
|
return getFiles(os_file_sep());
|
|
}
|
|
else
|
|
{
|
|
return getFiles(path);
|
|
}
|
|
#else
|
|
std::wstring path_lower=strlower(orig_path+os_file_sep());
|
|
std::vector<SMDir>::iterator it_dir=changed_dirs.end();
|
|
if(use_db)
|
|
{
|
|
it_dir=std::lower_bound(changed_dirs.begin(), changed_dirs.end(), SMDir(0, path_lower) );
|
|
if(it_dir!=changed_dirs.end() && (*it_dir).name!=path_lower)
|
|
it_dir=changed_dirs.end();
|
|
}
|
|
std::vector<SFile> tmp;
|
|
if(it_dir!=changed_dirs.end() || use_db==false)
|
|
{
|
|
++index_c_fs;
|
|
|
|
std::wstring tpath=path;
|
|
if(path.size()<2 || (path[0]!='\\' && path[1]!='\\' ) )
|
|
tpath=L"\\\\?\\"+path;
|
|
|
|
bool has_error;
|
|
tmp=getFiles(tpath, &has_error);
|
|
|
|
if(has_error)
|
|
{
|
|
VSSLog(L"Error while getting files in folder \""+path+L"\". SYSTEM probably does not have permissions to access this folder. Windows errorcode: "+convert((int)GetLastError()), LL_ERROR);
|
|
}
|
|
|
|
if(use_db)
|
|
{
|
|
std::vector<std::wstring> changed_files=cd->getChangedFiles((*it_dir).id);
|
|
std::sort(changed_files.begin(), changed_files.end());
|
|
|
|
if(!changed_files.empty())
|
|
{
|
|
for(size_t i=0;i<tmp.size();++i)
|
|
{
|
|
if(!tmp[i].isdir)
|
|
{
|
|
if( std::binary_search(changed_files.begin(), changed_files.end(), tmp[i].name ) )
|
|
{
|
|
tmp[i].last_modified*=Server->getRandomNumber();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(cd->hasFiles(path_lower) )
|
|
{
|
|
++index_c_db_update;
|
|
modifyFilesInt(path_lower, tmp);
|
|
}
|
|
else
|
|
{
|
|
cd->addFiles(path_lower, tmp);
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
else
|
|
{
|
|
if( cd->getFiles(path_lower, tmp) )
|
|
{
|
|
++index_c_db;
|
|
return tmp;
|
|
}
|
|
else
|
|
{
|
|
++index_c_fs;
|
|
|
|
std::wstring tpath=path;
|
|
if(path.size()<2 || (path[0]!='\\' && path[1]!='\\' ) )
|
|
tpath=L"\\\\?\\"+path;
|
|
|
|
tmp=getFiles(tpath);
|
|
cd->addFiles(path_lower, tmp);
|
|
return tmp;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
IPipe * IndexThread::getMsgPipe(void)
|
|
{
|
|
return msgpipe;
|
|
}
|
|
|
|
void IndexThread::stopIndex(void)
|
|
{
|
|
stop_index=true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool IndexThread::wait_for(IVssAsync *vsasync)
|
|
{
|
|
CHECK_COM_RESULT(vsasync->Wait());
|
|
|
|
HRESULT res;
|
|
CHECK_COM_RESULT(vsasync->QueryStatus(&res, NULL));
|
|
|
|
while(res==VSS_S_ASYNC_PENDING )
|
|
{
|
|
CHECK_COM_RESULT(vsasync->Wait());
|
|
|
|
CHECK_COM_RESULT(vsasync->QueryStatus(&res, NULL));
|
|
}
|
|
|
|
if( res!=VSS_S_ASYNC_FINISHED )
|
|
{
|
|
VSSLog("res!=VSS_S_ASYNC_FINISHED CCOM fail", LL_ERROR);
|
|
vsasync->Release();
|
|
return false;
|
|
}
|
|
vsasync->Release();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool IndexThread::start_shadowcopy(SCDirs *dir, bool *onlyref, bool restart_own, std::vector<SCRef*> no_restart_refs, bool for_imagebackup, bool *stale_shadowcopy)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifdef ENABLE_VSS
|
|
cleanup_saved_shadowcopies(true);
|
|
|
|
WCHAR volume_path[MAX_PATH];
|
|
BOOL ok = GetVolumePathNameW(dir->orig_target.c_str(), volume_path, MAX_PATH);
|
|
if(!ok)
|
|
{
|
|
VSSLog("GetVolumePathName(dir.target, volume_path, MAX_PATH) failed", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
std::wstring wpath=volume_path;
|
|
|
|
{
|
|
for(size_t i=0;i<sc_refs.size();++i)
|
|
{
|
|
if(sc_refs[i]->target==wpath && sc_refs[i]->ok)
|
|
{
|
|
bool do_restart=true;
|
|
bool found_in_no_restart_refs=false;
|
|
for(size_t k=0;k<no_restart_refs.size();++k)
|
|
{
|
|
if(no_restart_refs[k]==sc_refs[i])
|
|
{
|
|
found_in_no_restart_refs=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(found_in_no_restart_refs)
|
|
{
|
|
do_restart=false;
|
|
}
|
|
|
|
bool only_own_tokens=true;
|
|
for(size_t k=0;k<sc_refs[i]->starttokens.size();++k)
|
|
{
|
|
if( sc_refs[i]->starttokens[k]!=starttoken && (Server->getTimeSeconds()-ClientConnector::getLastTokenTime(sc_refs[i]->starttokens[k]))<3600)
|
|
{
|
|
only_own_tokens=false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
IFile *volf=Server->openFile(sc_refs[i]->volpath, MODE_READ);
|
|
if(volf==NULL)
|
|
{
|
|
do_restart=true;
|
|
VSSLog("Removing reference because shadowcopy could not be openend", LL_WARNING);
|
|
}
|
|
else
|
|
{
|
|
Server->destroy(volf);
|
|
}
|
|
|
|
if(Server->getTimeSeconds()-sc_refs[i]->starttime>shadowcopy_timeout/1000 || (do_restart==true && restart_own==true && only_own_tokens==true ) )
|
|
{
|
|
if( only_own_tokens==true)
|
|
{
|
|
VSSLog("Removing reference because restart own was specified and only own tokens are present", LL_WARNING);
|
|
}
|
|
else
|
|
{
|
|
VSSLog("Removing reference because of reference timeout", LL_WARNING);
|
|
}
|
|
|
|
std::vector<std::wstring> m_keys;
|
|
for(std::map<std::wstring, SCDirs*>::iterator lit=scdirs.begin();lit!=scdirs.end();++lit)
|
|
{
|
|
m_keys.push_back(lit->first);
|
|
}
|
|
SCRef *curr=sc_refs[i];
|
|
for(size_t k=0;k<m_keys.size();++k)
|
|
{
|
|
std::map<std::wstring, SCDirs*>::iterator it=scdirs.find(m_keys[k]);
|
|
if(it!=scdirs.end() && it->second->ref==curr)
|
|
{
|
|
VSSLog(L"Releasing "+it->first+L" orig_target="+it->second->orig_target+L" target="+it->second->target, LL_DEBUG);
|
|
release_shadowcopy(it->second, false, -1, dir);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
dir->ref=sc_refs[i];
|
|
if(!dir->ref->dontincrement)
|
|
{
|
|
dir->ref->rcount++;
|
|
sc_refs[i]->starttokens.push_back(starttoken);
|
|
}
|
|
else
|
|
{
|
|
dir->ref->dontincrement=false;
|
|
}
|
|
|
|
VSSLog(L"orig_target="+dir->orig_target+L" volpath="+dir->ref->volpath, LL_DEBUG);
|
|
|
|
dir->target=dir->orig_target;
|
|
dir->target.erase(0,wpath.size());
|
|
|
|
dir->target=dir->ref->volpath+os_file_sep()+dir->target;
|
|
if(dir->fileserv)
|
|
{
|
|
shareDir(dir->dir, dir->target);
|
|
}
|
|
|
|
if(for_imagebackup && dir->ref->save_id!=-1)
|
|
{
|
|
cd->modShadowcopyRefCount(dir->ref->save_id, 1);
|
|
}
|
|
|
|
if(onlyref!=NULL)
|
|
{
|
|
*onlyref=true;
|
|
}
|
|
|
|
if( stale_shadowcopy!=NULL )
|
|
{
|
|
if(found_in_no_restart_refs)
|
|
{
|
|
*stale_shadowcopy=false;
|
|
}
|
|
else if(only_own_tokens==false || restart_own==false)
|
|
{
|
|
*stale_shadowcopy=true;
|
|
}
|
|
}
|
|
|
|
VSSLog("Shadowcopy already present.", LL_DEBUG);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
dir->ref=new SCRef;
|
|
dir->ref->rcount=1;
|
|
dir->ref->starttime=Server->getTimeSeconds();
|
|
dir->ref->target=wpath;
|
|
dir->ref->starttokens.push_back(starttoken);
|
|
sc_refs.push_back(dir->ref);
|
|
}
|
|
|
|
if(dir->ref->rcount!=1)
|
|
{
|
|
VSSLog("Error rcount!=1", LL_ERROR);
|
|
}
|
|
|
|
IVssBackupComponents *backupcom=NULL;
|
|
CHECK_COM_RESULT_RELEASE(CreateVssBackupComponents(&backupcom));
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->InitializeForBackup());
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->SetBackupState(FALSE, TRUE, VSS_BT_FULL, FALSE));
|
|
|
|
IVssAsync *pb_result;
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->GatherWriterMetadata(&pb_result));
|
|
wait_for(pb_result);
|
|
|
|
#ifndef VSS_XP
|
|
#ifndef VSS_S03
|
|
CHECK_COM_RESULT_RELEASE(backupcom->SetContext(VSS_CTX_APP_ROLLBACK) );
|
|
#endif
|
|
#endif
|
|
|
|
std::wstring errmsg;
|
|
check_writer_status(backupcom, errmsg);
|
|
|
|
bool b_ok=true;
|
|
int tries=5;
|
|
while(b_ok==true)
|
|
{
|
|
HRESULT r;
|
|
CHECK_COM_RESULT_OK_HR(backupcom->StartSnapshotSet(&dir->ref->ssetid), b_ok, r);
|
|
if(b_ok)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(b_ok==false && tries>=0 && r==VSS_E_SNAPSHOT_SET_IN_PROGRESS )
|
|
{
|
|
VSSLog("Retrying starting shadow copy in 30s", LL_WARNING);
|
|
b_ok=true;
|
|
--tries;
|
|
}
|
|
Server->wait(30000);
|
|
}
|
|
|
|
if(!b_ok)
|
|
{
|
|
CHECK_COM_RESULT_RELEASE(backupcom->StartSnapshotSet(&dir->ref->ssetid));
|
|
}
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->AddToSnapshotSet(volume_path, GUID_NULL, &dir->ref->ssetid) );
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->PrepareForBackup(&pb_result));
|
|
wait_for(pb_result);
|
|
|
|
check_writer_status(backupcom, errmsg);
|
|
|
|
CHECK_COM_RESULT_RELEASE(backupcom->DoSnapshotSet(&pb_result));
|
|
wait_for(pb_result);
|
|
|
|
check_writer_status(backupcom, errmsg);
|
|
|
|
VSS_SNAPSHOT_PROP snap_props;
|
|
CHECK_COM_RESULT_RELEASE(backupcom->GetSnapshotProperties(dir->ref->ssetid, &snap_props));
|
|
|
|
dir->target.erase(0,wpath.size());
|
|
dir->ref->volpath=(std::wstring)snap_props.m_pwszSnapshotDeviceObject;
|
|
dir->starttime=Server->getTimeSeconds();
|
|
dir->target=dir->ref->volpath+os_file_sep()+dir->target;
|
|
if(dir->fileserv)
|
|
{
|
|
shareDir(dir->dir, dir->target);
|
|
}
|
|
|
|
SShadowCopy tsc;
|
|
tsc.vssid=snap_props.m_SnapshotId;
|
|
tsc.ssetid=snap_props.m_SnapshotSetId;
|
|
tsc.target=dir->orig_target;
|
|
tsc.path=(std::wstring)snap_props.m_pwszSnapshotDeviceObject;
|
|
tsc.orig_target=dir->orig_target;
|
|
tsc.filesrv=dir->fileserv;
|
|
tsc.vol=wpath;
|
|
tsc.tname=dir->dir;
|
|
tsc.starttoken=widen(starttoken);
|
|
if(for_imagebackup)
|
|
{
|
|
tsc.refs=1;
|
|
}
|
|
dir->ref->save_id=cd->addShadowcopy(tsc);
|
|
dir->ref->ok=true;
|
|
|
|
VSSLog(L"Shadowcopy path: "+tsc.path, LL_DEBUG);
|
|
|
|
dir->ref->ssetid=snap_props.m_SnapshotId;
|
|
|
|
VssFreeSnapshotProperties(&snap_props);
|
|
|
|
dir->ref->backupcom=backupcom;
|
|
if(onlyref!=NULL)
|
|
{
|
|
*onlyref=false;
|
|
}
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool IndexThread::release_shadowcopy(SCDirs *dir, bool for_imagebackup, int save_id, SCDirs *dontdel)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifdef ENABLE_VSS
|
|
|
|
if(for_imagebackup)
|
|
{
|
|
if(dir->ref!=NULL && dir->ref->save_id!=-1)
|
|
{
|
|
cd->modShadowcopyRefCount(dir->ref->save_id, -1);
|
|
}
|
|
else if(save_id!=-1)
|
|
{
|
|
cd->modShadowcopyRefCount(save_id, -1);
|
|
}
|
|
}
|
|
|
|
bool has_dels=false;
|
|
bool ok=false;
|
|
|
|
if(dir->ref!=NULL && dir->ref->backupcom!=NULL)
|
|
{
|
|
if(dir->ref->rcount<=1 || Server->getTimeSeconds()-dir->ref->starttime>shadowcopy_timeout/1000)
|
|
{
|
|
IVssBackupComponents *backupcom=dir->ref->backupcom;
|
|
IVssAsync *pb_result;
|
|
bool bcom_ok=true;
|
|
CHECK_COM_RESULT_OK(backupcom->BackupComplete(&pb_result), bcom_ok);
|
|
if(bcom_ok)
|
|
{
|
|
wait_for(pb_result);
|
|
}
|
|
|
|
std::wstring errmsg;
|
|
check_writer_status(backupcom, errmsg);
|
|
|
|
VSSLog(L"Deleting shadowcopy for path \""+dir->target+L"\" -2", LL_DEBUG);
|
|
|
|
if(dir->ref->save_id!=-1)
|
|
{
|
|
cd->deleteShadowcopy(dir->ref->save_id);
|
|
}
|
|
|
|
#ifndef VSS_XP
|
|
#ifndef VSS_S03
|
|
#ifndef SERVER_ONLY
|
|
|
|
if(bcom_ok)
|
|
{
|
|
LONG dels;
|
|
GUID ndels;
|
|
CHECK_COM_RESULT_OK(backupcom->DeleteSnapshots(dir->ref->ssetid, VSS_OBJECT_SNAPSHOT, TRUE,
|
|
&dels, &ndels), bcom_ok);
|
|
|
|
if(dels==0 || !bcom_ok)
|
|
{
|
|
VSSLog("Deleting shadowcopy failed.", LL_ERROR);
|
|
}
|
|
else
|
|
{
|
|
ok=true;
|
|
}
|
|
has_dels=true;
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if defined(VSS_XP) || defined(VSS_S03)
|
|
ok=true;
|
|
#endif
|
|
|
|
backupcom->Release();
|
|
dir->ref->backupcom=NULL;
|
|
dir->ref->rcount=1;
|
|
}
|
|
--dir->ref->rcount;
|
|
}
|
|
if(dir->ref!=NULL)
|
|
{
|
|
for(size_t k=0;k<dir->ref->starttokens.size();++k)
|
|
{
|
|
if(dir->ref->starttokens[k]==starttoken)
|
|
{
|
|
dir->ref->starttokens.erase(dir->ref->starttokens.begin()+k);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanup_saved_shadowcopies();
|
|
|
|
{
|
|
|
|
#if defined(VSS_XP) || defined(VSS_S03)
|
|
std::vector<SShadowCopy> scs=cd->getShadowcopies();
|
|
|
|
bool found=false;
|
|
|
|
for(size_t i=0;i<scs.size();++i)
|
|
{
|
|
if( scs[i].target==dir->target || ( dir->ref!=NULL && dir->ref->backupcom==NULL ) )
|
|
{
|
|
found=true;
|
|
}
|
|
}
|
|
|
|
if(found)
|
|
{
|
|
for(size_t i=0;i<scs.size();++i)
|
|
{
|
|
if(scs[i].target==dir->target || ( dir->ref!=NULL && dir->ref->backupcom==NULL ) )
|
|
{
|
|
VSSLog(L"Removing shadowcopy entry for path \""+scs[i].path+L"\"", LL_DEBUG);
|
|
cd->deleteShadowcopy(scs[i].id);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool r=true;
|
|
while(r)
|
|
{
|
|
r=false;
|
|
for(size_t i=0;i<sc_refs.size();++i)
|
|
{
|
|
if(sc_refs[i]->rcount<=0)
|
|
{
|
|
VSSLog(L"Deleting Shadowcopy for dir \""+sc_refs[i]->target+L"\"", LL_DEBUG);
|
|
bool c=true;
|
|
while(c)
|
|
{
|
|
c=false;
|
|
for(std::map<std::wstring, SCDirs*>::iterator it=scdirs.begin();it!=scdirs.end();++it)
|
|
{
|
|
if(it->second->ref==sc_refs[i])
|
|
{
|
|
if(it->second->fileserv)
|
|
{
|
|
shareDir(it->second->dir, it->second->orig_target);
|
|
}
|
|
it->second->target=it->second->orig_target;
|
|
|
|
it->second->ref=NULL;
|
|
if(dontdel==NULL || it->second!=dontdel )
|
|
{
|
|
delete it->second;
|
|
scdirs.erase(it);
|
|
c=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete sc_refs[i];
|
|
sc_refs.erase(sc_refs.begin()+i);
|
|
r=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(has_dels)
|
|
{
|
|
return ok;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
#else
|
|
return true;
|
|
#endif
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool IndexThread::cleanup_saved_shadowcopies(bool start)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifdef ENABLE_VSS
|
|
#ifndef VSS_XP
|
|
#ifndef VSS_S03
|
|
#ifndef SERVER_ONLY
|
|
std::vector<SShadowCopy> scs=cd->getShadowcopies();
|
|
|
|
bool found=false;
|
|
|
|
for(size_t i=0;i<scs.size();++i)
|
|
{
|
|
bool f2=true;
|
|
for(size_t j=0;j<sc_refs.size();++j)
|
|
{
|
|
if(sc_refs[j]->save_id==scs[i].id)
|
|
{
|
|
f2=false;
|
|
break;
|
|
}
|
|
}
|
|
if(f2==true && (scs[i].refs<=0 || scs[i].passedtime>shadowcopy_timeout/1000 || (start && scs[i].filesrv==false && scs[i].refs==1 && !starttoken.empty() && scs[i].starttoken==widen(starttoken) ) ) )
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if(found)
|
|
{
|
|
bool ok=false;
|
|
IVssBackupComponents *backupcom=NULL;
|
|
CHECK_COM_RESULT_RELEASE(CreateVssBackupComponents(&backupcom));
|
|
CHECK_COM_RESULT_RELEASE(backupcom->InitializeForBackup());
|
|
CHECK_COM_RESULT_RELEASE(backupcom->SetContext(VSS_CTX_APP_ROLLBACK) );
|
|
|
|
for(size_t i=0;i<scs.size();++i)
|
|
{
|
|
bool f2=true;
|
|
for(size_t j=0;j<sc_refs.size();++j)
|
|
{
|
|
if(sc_refs[j]->save_id==scs[i].id)
|
|
{
|
|
f2=false;
|
|
break;
|
|
}
|
|
}
|
|
if( f2==true && (scs[i].refs<=0 || scs[i].passedtime>shadowcopy_timeout/1000 || (start && scs[i].filesrv==false && scs[i].refs==1 && !starttoken.empty() &&scs[i].starttoken==widen(starttoken) ) ) )
|
|
{
|
|
VSSLog(L"Deleting shadowcopy for path \""+scs[i].path+L"\"", LL_DEBUG);
|
|
LONG dels;
|
|
GUID ndels;
|
|
CHECK_COM_RESULT(backupcom->DeleteSnapshots(scs[i].vssid, VSS_OBJECT_SNAPSHOT, TRUE,
|
|
&dels, &ndels));
|
|
cd->deleteShadowcopy(scs[i].id);
|
|
if(dels>0)
|
|
ok=true;
|
|
}
|
|
}
|
|
|
|
backupcom->Release();
|
|
return ok;
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif //ENABLE_VSS
|
|
#endif //_WIN32
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#ifdef ENABLE_VSS
|
|
|
|
bool IndexThread::checkErrorAndLog(BSTR pbstrWriter, VSS_WRITER_STATE pState, HRESULT pHrResultFailure, std::wstring& errmsg)
|
|
{
|
|
#define FAIL_STATE(x) case x: { state=L#x; failure=true; } break
|
|
#define OK_STATE(x) case x: { state=L#x; } break
|
|
|
|
std::wstring state;
|
|
bool failure=false;
|
|
switch(pState)
|
|
{
|
|
FAIL_STATE(VSS_WS_UNKNOWN);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_IDENTIFY);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_PREPARE_BACKUP);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_PREPARE_SNAPSHOT);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_FREEZE);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_THAW);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_POST_SNAPSHOT);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_BACKUP_COMPLETE);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_PRE_RESTORE);
|
|
FAIL_STATE(VSS_WS_FAILED_AT_POST_RESTORE);
|
|
#ifndef VSS_XP
|
|
#ifndef VSS_S03
|
|
FAIL_STATE(VSS_WS_FAILED_AT_BACKUPSHUTDOWN);
|
|
#endif
|
|
#endif
|
|
OK_STATE(VSS_WS_STABLE);
|
|
OK_STATE(VSS_WS_WAITING_FOR_FREEZE);
|
|
OK_STATE(VSS_WS_WAITING_FOR_THAW);
|
|
OK_STATE(VSS_WS_WAITING_FOR_POST_SNAPSHOT);
|
|
OK_STATE(VSS_WS_WAITING_FOR_BACKUP_COMPLETE);
|
|
}
|
|
|
|
#undef FAIL_STATE
|
|
#undef OK_STATE
|
|
|
|
std::wstring err;
|
|
bool has_error=false;
|
|
#define HR_ERR(x) case x: { err=L#x; has_error=true; } break
|
|
switch(pHrResultFailure)
|
|
{
|
|
case S_OK: { err=L"S_OK"; } break;
|
|
HR_ERR(VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT);
|
|
HR_ERR(VSS_E_WRITERERROR_OUTOFRESOURCES);
|
|
HR_ERR(VSS_E_WRITERERROR_TIMEOUT);
|
|
HR_ERR(VSS_E_WRITERERROR_RETRYABLE);
|
|
HR_ERR(VSS_E_WRITERERROR_NONRETRYABLE);
|
|
HR_ERR(VSS_E_WRITER_NOT_RESPONDING);
|
|
#ifndef VSS_XP
|
|
#ifndef VSS_S03
|
|
HR_ERR(VSS_E_WRITER_STATUS_NOT_AVAILABLE);
|
|
#endif
|
|
#endif
|
|
}
|
|
#undef HR_ERR
|
|
|
|
if(failure || has_error)
|
|
{
|
|
std::wstring nerrmsg=L"Writer "+std::wstring(pbstrWriter)+L" has failure state "+state+L" with error "+err+L". UrBackup will continue with the backup but the associated data may not be consistent.";
|
|
VSSLog(nerrmsg, LL_ERROR);
|
|
errmsg+=nerrmsg;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
VSSLog(L"Writer "+std::wstring(pbstrWriter)+L" has failure state "+state+L" with error "+err+L".", LL_DEBUG);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool IndexThread::check_writer_status(IVssBackupComponents *backupcom, std::wstring& errmsg)
|
|
{
|
|
IVssAsync *pb_result;
|
|
CHECK_COM_RESULT_RETURN(backupcom->GatherWriterStatus(&pb_result));
|
|
|
|
if(!wait_for(pb_result))
|
|
{
|
|
VSSLog("Error while waiting for result from GatherWriterStatus", LL_ERROR);
|
|
}
|
|
|
|
UINT nWriters;
|
|
CHECK_COM_RESULT_RETURN(backupcom->GetWriterStatusCount(&nWriters));
|
|
|
|
VSSLog("Number of Writers: "+nconvert(nWriters), LL_DEBUG);
|
|
|
|
bool has_error=false;
|
|
for(UINT i=0;i<nWriters;++i)
|
|
{
|
|
VSS_ID pidInstance;
|
|
VSS_ID pidWriter;
|
|
BSTR pbstrWriter;
|
|
VSS_WRITER_STATE pState;
|
|
HRESULT pHrResultFailure;
|
|
|
|
bool ok=true;
|
|
CHECK_COM_RESULT_OK(backupcom->GetWriterStatus(i,
|
|
&pidInstance,
|
|
&pidWriter,
|
|
&pbstrWriter,
|
|
&pState,
|
|
&pHrResultFailure), ok);
|
|
|
|
if(ok)
|
|
{
|
|
if(!checkErrorAndLog(pbstrWriter, pState, pHrResultFailure, errmsg))
|
|
{
|
|
has_error=true;
|
|
}
|
|
|
|
SysFreeString(pbstrWriter);
|
|
}
|
|
}
|
|
|
|
CHECK_COM_RESULT_RETURN(backupcom->FreeWriterStatus());
|
|
|
|
return !has_error;
|
|
}
|
|
#endif //ENABLE_VSS
|
|
#endif //_WIN32
|
|
|
|
#ifdef _WIN32
|
|
std::string IndexThread::GetErrorHResErrStr(HRESULT res)
|
|
{
|
|
switch(res)
|
|
{
|
|
case E_INVALIDARG:
|
|
return "E_INVALIDARG";
|
|
case E_OUTOFMEMORY:
|
|
return "E_OUTOFMEMORY";
|
|
case E_UNEXPECTED:
|
|
return "E_UNEXPECTED";
|
|
case E_ACCESSDENIED:
|
|
return "E_ACCESSDENIED";
|
|
case VSS_E_OBJECT_NOT_FOUND:
|
|
return "VSS_E_OBJECT_NOT_FOUND";
|
|
case VSS_E_PROVIDER_VETO:
|
|
return "VSS_E_PROVIDER_VETO";
|
|
case VSS_E_UNEXPECTED_PROVIDER_ERROR:
|
|
return "VSS_E_UNEXPECTED_PROVIDER_ERROR";
|
|
case VSS_E_BAD_STATE:
|
|
return "VSS_E_BAD_STATE";
|
|
case VSS_E_SNAPSHOT_SET_IN_PROGRESS:
|
|
return "VSS_E_SNAPSHOT_SET_IN_PROGRESS";
|
|
case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED:
|
|
return "VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED";
|
|
case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED:
|
|
return "VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED";
|
|
case VSS_E_PROVIDER_NOT_REGISTERED:
|
|
return "VSS_E_PROVIDER_NOT_REGISTERED";
|
|
case VSS_E_VOLUME_NOT_SUPPORTED:
|
|
return "VSS_E_VOLUME_NOT_SUPPORTED";
|
|
case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER:
|
|
return "VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER";
|
|
};
|
|
return "UNDEF";
|
|
}
|
|
#endif
|
|
|
|
std::string IndexThread::lookup_shadowcopy(int sid)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifndef SERVER_ONLY
|
|
std::vector<SShadowCopy> scs=cd->getShadowcopies();
|
|
|
|
for(size_t i=0;i<scs.size();++i)
|
|
{
|
|
if(scs[i].id==sid)
|
|
{
|
|
IVssBackupComponents *backupcom=NULL;
|
|
CHECK_COM_RESULT_RELEASE_S(CreateVssBackupComponents(&backupcom));
|
|
CHECK_COM_RESULT_RELEASE_S(backupcom->InitializeForBackup());
|
|
CHECK_COM_RESULT_RELEASE_S(backupcom->SetContext(VSS_CTX_APP_ROLLBACK) );
|
|
VSS_SNAPSHOT_PROP snap_props;
|
|
CHECK_COM_RESULT_RELEASE_S(backupcom->GetSnapshotProperties(scs[i].vssid, &snap_props));
|
|
std::string ret=Server->ConvertToUTF8(snap_props.m_pwszSnapshotDeviceObject);
|
|
VssFreeSnapshotProperties(&snap_props);
|
|
backupcom->Release();
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
return "";
|
|
}
|
|
|
|
SCDirs* IndexThread::getSCDir(const std::wstring path)
|
|
{
|
|
std::map<std::wstring, SCDirs*>::iterator it=scdirs.find(path);
|
|
if(it!=scdirs.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
else
|
|
{
|
|
SCDirs *nd=new SCDirs;
|
|
scdirs.insert(std::pair<std::wstring, SCDirs*>(path, nd) );
|
|
|
|
nd->running=false;
|
|
|
|
return nd;
|
|
}
|
|
}
|
|
|
|
IFileServ *IndexThread::getFileSrv(void)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
return filesrv;
|
|
}
|
|
|
|
void IndexThread::execute_prebackup_hook(void)
|
|
{
|
|
#ifdef _WIN32
|
|
system(Server->ConvertToUTF8(L"\""+Server->getServerWorkingDir()+L"\\prefilebackup.bat\"").c_str());
|
|
#else
|
|
system("/etc/urbackup/prefilebackup");
|
|
#endif
|
|
}
|
|
|
|
void IndexThread::execute_postindex_hook(void)
|
|
{
|
|
#ifdef _WIN32
|
|
system(Server->ConvertToUTF8(L"\""+Server->getServerWorkingDir()+L"\\postfileindex.bat\"").c_str());
|
|
#else
|
|
system("/etc/urbackup/postfileindex");
|
|
#endif
|
|
}
|
|
|
|
void IndexThread::execute_postbackup_hook(void)
|
|
{
|
|
#ifdef _WIN32
|
|
STARTUPINFOW si;
|
|
PROCESS_INFORMATION pi;
|
|
memset(&si, 0, sizeof(STARTUPINFO) );
|
|
memset(&pi, 0, sizeof(PROCESS_INFORMATION) );
|
|
si.cb=sizeof(STARTUPINFO);
|
|
if(!CreateProcessW(L"C:\\Windows\\system32\\cmd.exe", (LPWSTR)(L"cmd.exe /C \""+Server->getServerWorkingDir()+L"\\postfilebackup.bat\"").c_str(), NULL, NULL, false, NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW, NULL, NULL, &si, &pi) )
|
|
{
|
|
Server->Log("Executing postfilebackup.bat failed: "+nconvert((int)GetLastError()), LL_INFO);
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
#else
|
|
pid_t pid1;
|
|
pid1 = fork();
|
|
if( pid1==0 )
|
|
{
|
|
setsid();
|
|
pid_t pid2;
|
|
pid2 = fork();
|
|
if(pid2==0)
|
|
{
|
|
char *a1=(char*)"/etc/urbackup/postfilebackup";
|
|
char* const argv[]={ a1, NULL };
|
|
execv(a1, argv);
|
|
Server->Log("Error in execv /etc/urbackup/postfilebackup: "+nconvert(errno), LL_INFO);
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int status;
|
|
waitpid(pid1, &status, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::wstring IndexThread::sanitizePattern(const std::wstring &p)
|
|
{
|
|
std::wstring ep=trim(p);
|
|
std::wstring nep;
|
|
nep.reserve(ep.size()*2);
|
|
for(size_t j=0;j<ep.size();++j)
|
|
{
|
|
wchar_t ch=ep[j];
|
|
if(ch=='/')
|
|
{
|
|
if(os_file_sep()==L"\\")
|
|
{
|
|
nep+=L"\\\\";
|
|
}
|
|
else
|
|
{
|
|
nep+=os_file_sep();
|
|
}
|
|
}
|
|
else if(ch=='\\' && j+1<ep.size() && ep[j+1]=='\\')
|
|
{
|
|
if(os_file_sep()==L"\\")
|
|
{
|
|
nep+=L"\\\\";
|
|
}
|
|
else
|
|
{
|
|
nep+=os_file_sep();
|
|
}
|
|
++j;
|
|
}
|
|
else if(ch=='\\' && ( j+1>=ep.size() || (ep[j+1]!='[' ) ) )
|
|
{
|
|
if(os_file_sep()==L"\\")
|
|
nep+=L"\\\\";
|
|
else
|
|
nep+=os_file_sep();
|
|
}
|
|
else
|
|
{
|
|
nep+=ch;
|
|
}
|
|
}
|
|
return nep;
|
|
}
|
|
|
|
void IndexThread::readPatterns(bool &pattern_changed, bool update_saved_patterns)
|
|
{
|
|
ISettingsReader *curr_settings=Server->createFileSettingsReader("urbackup/data/settings.cfg");
|
|
exlude_dirs.clear();
|
|
if(curr_settings!=NULL)
|
|
{
|
|
std::wstring val;
|
|
if(curr_settings->getValue(L"exclude_files", &val) || curr_settings->getValue(L"exclude_files_def", &val) )
|
|
{
|
|
if(val!=cd->getOldExcludePattern())
|
|
{
|
|
pattern_changed=true;
|
|
if(update_saved_patterns)
|
|
{
|
|
cd->updateOldExcludePattern(val);
|
|
}
|
|
}
|
|
|
|
std::vector<std::wstring> toks;
|
|
Tokenize(val, toks, L";");
|
|
exlude_dirs=toks;
|
|
#ifdef _WIN32
|
|
for(size_t i=0;i<exlude_dirs.size();++i)
|
|
{
|
|
strupper(&exlude_dirs[i]);
|
|
}
|
|
#endif
|
|
for(size_t i=0;i<exlude_dirs.size();++i)
|
|
{
|
|
if(exlude_dirs[i].find('\\')==std::wstring::npos
|
|
&& exlude_dirs[i].find('/')==std::wstring::npos
|
|
&& exlude_dirs[i].find('*')==std::wstring::npos )
|
|
{
|
|
exlude_dirs[i]=L"*/"+trim(exlude_dirs[i]);
|
|
}
|
|
}
|
|
for(size_t i=0;i<exlude_dirs.size();++i)
|
|
{
|
|
exlude_dirs[i]=sanitizePattern(exlude_dirs[i]);
|
|
}
|
|
|
|
}
|
|
if(curr_settings->getValue(L"include_files", &val) || curr_settings->getValue(L"include_files_def", &val) )
|
|
{
|
|
if(val!=cd->getOldIncludePattern())
|
|
{
|
|
pattern_changed=true;
|
|
if(update_saved_patterns)
|
|
{
|
|
cd->updateOldIncludePattern(val);
|
|
}
|
|
}
|
|
|
|
std::vector<std::wstring> toks;
|
|
Tokenize(val, toks, L";");
|
|
include_dirs=toks;
|
|
#ifdef _WIN32
|
|
for(size_t i=0;i<include_dirs.size();++i)
|
|
{
|
|
strupper(&include_dirs[i]);
|
|
}
|
|
#endif
|
|
for(size_t i=0;i<include_dirs.size();++i)
|
|
{
|
|
include_dirs[i]=sanitizePattern(include_dirs[i]);
|
|
}
|
|
include_depth.resize(include_dirs.size());
|
|
for(size_t i=0;i<include_dirs.size();++i)
|
|
{
|
|
std::wstring ip=include_dirs[i];
|
|
if(ip.find(L"*")==ip.size()-1 || ip.find(L"*")==std::string::npos)
|
|
{
|
|
int depth=0;
|
|
for(size_t j=0;j<ip.size();++j)
|
|
{
|
|
if(ip[j]=='/')
|
|
++depth;
|
|
else if(ip[j]=='\\' && j+1<ip.size() && ip[j+1]=='\\')
|
|
{
|
|
++j;
|
|
++depth;
|
|
}
|
|
}
|
|
include_depth[i]=depth;
|
|
}
|
|
else
|
|
{
|
|
include_depth[i]=-1;
|
|
}
|
|
}
|
|
include_prefix.resize(include_dirs.size());
|
|
for(size_t i=0;i<include_dirs.size();++i)
|
|
{
|
|
size_t f1=include_dirs[i].find_first_of(L":");
|
|
size_t f2=include_dirs[i].find_first_of(L"[");
|
|
size_t f3=include_dirs[i].find_first_of(L"*");
|
|
while(f2>0 && f2!=std::string::npos && include_dirs[i][f2-1]=='\\')
|
|
f2=include_dirs[i].find_first_of(L"[", f2);
|
|
|
|
size_t f=(std::min)((std::min)(f1,f2), f3);
|
|
|
|
if(f!=std::string::npos)
|
|
{
|
|
if(f>0)
|
|
{
|
|
include_prefix[i]=include_dirs[i].substr(0, f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
include_prefix[i]=include_dirs[i];
|
|
}
|
|
|
|
std::wstring nep;
|
|
for(size_t j=0;j<include_prefix[i].size();++j)
|
|
{
|
|
wchar_t ch=include_prefix[i][j];
|
|
if(ch=='/')
|
|
nep+=os_file_sep();
|
|
else if(ch=='\\' && j+1<include_prefix[i].size() && include_prefix[i][j+1]=='\\')
|
|
{
|
|
nep+=os_file_sep();
|
|
++j;
|
|
}
|
|
else
|
|
{
|
|
nep+=ch;
|
|
}
|
|
}
|
|
|
|
include_prefix[i]=nep;
|
|
}
|
|
|
|
}
|
|
Server->destroy(curr_settings);
|
|
}
|
|
}
|
|
|
|
bool amatch(const wchar_t *str, const wchar_t *p);
|
|
|
|
bool IndexThread::isExcluded(const std::wstring &path)
|
|
{
|
|
std::wstring wpath=path;
|
|
#ifdef _WIN32
|
|
strupper(&wpath);
|
|
#endif
|
|
for(size_t i=0;i<exlude_dirs.size();++i)
|
|
{
|
|
if(!exlude_dirs[i].empty())
|
|
{
|
|
bool b=amatch(wpath.c_str(), exlude_dirs[i].c_str());
|
|
if(b)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IndexThread::isIncluded(const std::wstring &path, bool *adding_worthless)
|
|
{
|
|
std::wstring wpath=path;
|
|
#ifdef _WIN32
|
|
strupper(&wpath);
|
|
#endif
|
|
int wpath_level=0;
|
|
if(adding_worthless!=NULL)
|
|
{
|
|
for(size_t i=0;i<wpath.size();++i)
|
|
{
|
|
if(wpath[i]=='/')
|
|
++wpath_level;
|
|
else if(wpath[i]=='\\')
|
|
++wpath_level;
|
|
else if(i==wpath.size()-1)
|
|
++wpath_level;
|
|
}
|
|
*adding_worthless=true;
|
|
}
|
|
bool has_pattern=false;
|
|
for(size_t i=0;i<include_dirs.size();++i)
|
|
{
|
|
if(!include_dirs[i].empty())
|
|
{
|
|
has_pattern=true;
|
|
bool b=amatch(wpath.c_str(), include_dirs[i].c_str());
|
|
if(b)
|
|
{
|
|
return true;
|
|
}
|
|
if(adding_worthless!=NULL)
|
|
{
|
|
if( include_depth[i]==-1 )
|
|
{
|
|
*adding_worthless=false;
|
|
}
|
|
else
|
|
{
|
|
bool has_prefix=(wpath.find(include_prefix[i])==0);
|
|
if( has_prefix )
|
|
{
|
|
if( wpath_level<=include_depth[i])
|
|
{
|
|
*adding_worthless=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return !has_pattern;
|
|
}
|
|
|
|
void IndexThread::start_filesrv(void)
|
|
{
|
|
std::wstring name;
|
|
if(Server->getServerParameter("restore_mode")=="true")
|
|
{
|
|
name=L"##restore##"+convert(Server->getTimeSeconds())+convert(Server->getRandomNumber()%10000);
|
|
writestring(Server->ConvertToUTF8(name), "clientname.txt");
|
|
}
|
|
else
|
|
{
|
|
ISettingsReader *curr_settings=Server->createFileSettingsReader("urbackup/data/settings.cfg");
|
|
if(curr_settings!=NULL)
|
|
{
|
|
std::wstring val;
|
|
if(curr_settings->getValue(L"computername", &val))
|
|
{
|
|
if(!val.empty())
|
|
{
|
|
name=val;
|
|
}
|
|
}
|
|
Server->destroy(curr_settings);
|
|
}
|
|
}
|
|
filesrv=((IFileServFactory*)(Server->getPlugin(Server->getThreadID(), filesrv_pluginid)))->createFileServ(tcpport, udpport, name);
|
|
filesrv->shareDir(L"urbackup", Server->getServerWorkingDir()+L"/urbackup/data");
|
|
|
|
ServerIdentityMgr::setFileServ(filesrv);
|
|
ServerIdentityMgr::loadServerIdentities();
|
|
}
|
|
|
|
void IndexThread::shareDir(const std::wstring &name, const std::wstring &path)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
filesrv_share_dirs[name]=path;
|
|
}
|
|
|
|
void IndexThread::removeDir(const std::wstring &name)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
std::map<std::wstring, std::wstring>::iterator it=filesrv_share_dirs.find(name);
|
|
if(it!=filesrv_share_dirs.end())
|
|
{
|
|
filesrv_share_dirs.erase(it);
|
|
}
|
|
}
|
|
|
|
std::wstring IndexThread::getShareDir(const std::wstring &name)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
return filesrv_share_dirs[name];
|
|
}
|
|
|
|
void IndexThread::share_dirs(const std::string &token)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
for(std::map<std::wstring, std::wstring>::iterator it=filesrv_share_dirs.begin();it!=filesrv_share_dirs.end();++it)
|
|
{
|
|
std::wstring dir=it->first;
|
|
if(!token.empty())
|
|
dir=widen(token)+L"|"+dir;
|
|
|
|
filesrv->shareDir(dir, it->second);
|
|
}
|
|
}
|
|
|
|
void IndexThread::unshare_dirs(const std::string &token)
|
|
{
|
|
IScopedLock lock(filesrv_mutex);
|
|
for(std::map<std::wstring, std::wstring>::iterator it=filesrv_share_dirs.begin();it!=filesrv_share_dirs.end();++it)
|
|
{
|
|
std::wstring dir=it->first;
|
|
if(!token.empty())
|
|
dir=widen(token)+L"|"+dir;
|
|
|
|
filesrv->removeDir(dir);
|
|
}
|
|
}
|
|
|
|
void IndexThread::doStop(void)
|
|
{
|
|
CWData wd;
|
|
wd.addUChar(8);
|
|
wd.addVoidPtr(NULL);
|
|
msgpipe->Write(wd.getDataPtr(), wd.getDataSize());
|
|
}
|
|
|
|
void IndexThread::modifyFilesInt(std::wstring path, const std::vector<SFile> &data)
|
|
{
|
|
size_t add_size=path.size()*sizeof(wchar_t)+sizeof(std::wstring);
|
|
for(size_t i=0;i<data.size();++i)
|
|
{
|
|
add_size+=data[i].name.size()*sizeof(wchar_t);
|
|
add_size+=sizeof(SFile);
|
|
}
|
|
add_size+=sizeof(std::vector<SFile>);
|
|
|
|
modify_file_buffer_size+=add_size;
|
|
|
|
modify_file_buffer.push_back(std::pair<std::wstring, std::vector<SFile> >(path, data) );
|
|
|
|
if( modify_file_buffer_size>max_modify_file_buffer_size)
|
|
{
|
|
commitModifyFilesBuffer();
|
|
}
|
|
}
|
|
|
|
void IndexThread::commitModifyFilesBuffer(void)
|
|
{
|
|
db->BeginTransaction();
|
|
for(size_t i=0;i<modify_file_buffer.size();++i)
|
|
{
|
|
cd->modifyFiles(modify_file_buffer[i].first, modify_file_buffer[i].second);
|
|
}
|
|
db->EndTransaction();
|
|
|
|
modify_file_buffer.clear();
|
|
modify_file_buffer_size=0;
|
|
}
|
|
|
|
std::wstring IndexThread::removeDirectorySeparatorAtEnd(const std::wstring& path)
|
|
{
|
|
wchar_t path_sep=os_file_sep()[0];
|
|
if(!path.empty() && path[path.size()-1]==path_sep )
|
|
{
|
|
return path.substr(0, path.size()-1);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
std::string IndexThread::getSHA256(const std::wstring& fn)
|
|
{
|
|
sha256_ctx ctx;
|
|
sha256_init(&ctx);
|
|
|
|
IFile * f=Server->openFile(os_file_prefix(fn), MODE_READ_SEQUENTIAL);
|
|
|
|
if(f==NULL)
|
|
{
|
|
return std::string();
|
|
}
|
|
|
|
char buffer[32768];
|
|
unsigned int r;
|
|
while( (r=f->Read(buffer, 4096))>0)
|
|
{
|
|
sha256_update(&ctx, reinterpret_cast<const unsigned char*>(buffer), r);
|
|
}
|
|
|
|
Server->destroy(f);
|
|
|
|
unsigned char dig[32];
|
|
sha256_final(&ctx, dig);
|
|
|
|
return bytesToHex(dig, 32);
|
|
}
|
|
|
|
std::string IndexThread::getSHA512Binary(const std::wstring& fn)
|
|
{
|
|
Server->Log(L"Calculating SHA512 Hash for file \""+fn+L"\"", LL_DEBUG);
|
|
sha512_ctx ctx;
|
|
sha512_init(&ctx);
|
|
|
|
IFile * f=Server->openFile(os_file_prefix(fn), MODE_READ_SEQUENTIAL);
|
|
|
|
if(f==NULL)
|
|
{
|
|
return std::string();
|
|
}
|
|
|
|
char buffer[32768];
|
|
unsigned int r;
|
|
while( (r=f->Read(buffer, 4096))>0)
|
|
{
|
|
sha512_update(&ctx, reinterpret_cast<const unsigned char*>(buffer), r);
|
|
}
|
|
|
|
Server->destroy(f);
|
|
|
|
std::string ret;
|
|
ret.resize(64);
|
|
sha512_final(&ctx, reinterpret_cast<unsigned char*>(const_cast<char*>(ret.c_str())));
|
|
return ret;
|
|
}
|
|
|
|
void IndexThread::VSSLog(const std::string& msg, int loglevel)
|
|
{
|
|
Server->Log(msg, loglevel);
|
|
if(loglevel>LL_DEBUG)
|
|
{
|
|
vsslog.push_back(std::make_pair(msg, loglevel));
|
|
}
|
|
}
|
|
|
|
void IndexThread::VSSLog(const std::wstring& msg, int loglevel)
|
|
{
|
|
Server->Log(msg, loglevel);
|
|
if(loglevel>LL_DEBUG)
|
|
{
|
|
vsslog.push_back(std::make_pair(Server->ConvertToUTF8(msg), loglevel));
|
|
}
|
|
}
|
|
|
|
void IndexThread::modifyHashInt(const std::string& hash, const std::wstring& path, int64 filesize, int64 modifytime)
|
|
{
|
|
size_t add_size=sizeof(SHashedFile);
|
|
|
|
modify_hash_buffer.push_back(SHashedFile(path, filesize, modifytime, hash));
|
|
add_size+=hash.size()+path.size();
|
|
|
|
modify_hash_buffer_size+=add_size;
|
|
|
|
if(modify_hash_buffer_size>max_modify_hash_buffer_size)
|
|
{
|
|
commitModifyHashBuffer();
|
|
}
|
|
}
|
|
|
|
void IndexThread::commitModifyHashBuffer(void)
|
|
{
|
|
db->BeginTransaction();
|
|
for(size_t i=0;i<modify_hash_buffer.size();++i)
|
|
{
|
|
cd->modifyFileHash(modify_hash_buffer[i].hash, modify_hash_buffer[i].filesize, modify_hash_buffer[i].modifytime, modify_hash_buffer[i].path);
|
|
}
|
|
db->EndTransaction();
|
|
|
|
modify_hash_buffer.clear();
|
|
modify_hash_buffer_size=0;
|
|
} |