urbackup_backend/urbackupclient/client.cpp
2013-05-29 21:35:21 +02:00

2162 lines
49 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;
#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;
}
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);");
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);
//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);
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);
IQuery *q=db->Prepare("DELETE FROM files", false);
q->Write();
db->destroyQuery(q);
}
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);
IQuery *q=db->Prepare("DELETE FROM files", false);
q->Write();
db->destroyQuery(q);
}
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;
{
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();
commitModifyFilesBuffer();
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();
commitModifyFilesBuffer();
#ifdef _WIN32
if(!has_stale_shadowcopy)
{
VSSLog("Deleting backup of changed dirs...", LL_DEBUG);
cd->deleteSavedChangedDirs();
cd->deleteSavedDelDirs();
cd->deleteSavedChangedFiles();
}
else
{
VSSLog("Do 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)
{
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_INFO);
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(TRUE, TRUE, VSS_BT_FULL, FALSE));
std::wstring errmsg;
if(!check_writer_status(backupcom, errmsg))
{
return false;
}
#ifndef VSS_XP
#ifndef VSS_S03
CHECK_COM_RESULT_RELEASE(backupcom->SetContext(VSS_CTX_APP_ROLLBACK) );
#endif
#endif
IVssAsync *pb_result;
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);
if(!check_writer_status(backupcom, errmsg))
{
return false;
}
CHECK_COM_RESULT_RELEASE(backupcom->DoSnapshotSet(&pb_result));
wait_for(pb_result);
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);
FAIL_STATE(VSS_WS_FAILED_AT_BACKUPSHUTDOWN);
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;
#define HR_ERR(x) case x: { err=L#x; } 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);
HR_ERR(VSS_E_WRITER_STATUS_NOT_AVAILABLE);
}
#undef HR_ERR
if(failure)
{
std::wstring nerrmsg=L"Writer "+std::wstring(pbstrWriter)+L" has failure state "+state+L" with error "+err+L". ";
VSSLog(nerrmsg, LL_ERROR);
errmsg+=nerrmsg;
return false;
}
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));
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;
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);
}
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);
if(f==NULL)
{
return std::string();
}
char buffer[4096];
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);
}
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));
}
}