std::wstring ChangeJournalWatcher::getFilename(_i64 frn, _i64 rid) { std::wstring path; _i64 curr_id=frn; while(true) { q_get_name->Bind(curr_id); q_get_name->Bind(rid); db_results res=q_get_name->Read(); q_get_name->Reset(); if(!res.empty()) { _i64 pid=os_atoi64(wnarrow(res[0][L"pid"])); if(pid!=-1) { path=res[0][L"name"]+os_file_sep()+path; curr_id=pid; } else { path=res[0][L"name"]+os_file_sep()+path; break; } } else { Server->Log(L"Couldn't follow up to root. Current path: "+path, LL_ERROR); return L""; } } return path; }
void saveGeneralSettings(str_map &GET, IDatabase *db) { IQuery *q_get=db->Prepare("SELECT value FROM settings_db.settings WHERE clientid=0 AND key=?"); IQuery *q_update=db->Prepare("UPDATE settings_db.settings SET value=? WHERE key=? AND clientid=0"); IQuery *q_insert=db->Prepare("INSERT INTO settings_db.settings (key, value, clientid) VALUES (?,?,0)"); std::vector<std::wstring> settings=getGlobalSettingsList(); for(size_t i=0;i<settings.size();++i) { str_map::iterator it=GET.find(settings[i]); if(it!=GET.end()) { updateSetting(settings[i], it->second, q_get, q_update, q_insert); } } #ifdef _WIN32 std::wstring tmpdir=GET[L"tmpdir"]; if(!tmpdir.empty()) { os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp"); Server->setTemporaryDirectory(tmpdir+os_file_sep()+L"urbackup_tmp"); } #endif }
bool CopyFolder(std::wstring src, std::wstring dst) { if(!os_create_dir(dst)) return false; std::vector<SFile> curr_files=getFiles(src); for(size_t i=0;i<curr_files.size();++i) { if(curr_files[i].isdir) { bool b=CopyFolder(src+os_file_sep()+curr_files[i].name, dst+os_file_sep()+curr_files[i].name); if(!b) return false; } else { if(!os_create_hardlink(dst+os_file_sep()+curr_files[i].name, src+os_file_sep()+curr_files[i].name, false, NULL) ) { BOOL b=CopyFileW( (src+os_file_sep()+curr_files[i].name).c_str(), (dst+os_file_sep()+curr_files[i].name).c_str(), FALSE); if(!b) { return false; } } } } return true; }
void ChangeJournalWatcher::indexRootDirs(_i64 rid, const std::wstring &root, _i64 parent) { if(indexing_in_progress) { if(Server->getTimeMS()-last_index_update>10000) { update(); last_index_update=Server->getTimeMS(); } } std::wstring dir=root+os_file_sep(); HANDLE hDir; if(root.size()==2) hDir=CreateFileW(dir.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); else hDir=CreateFileW(root.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(INVALID_HANDLE_VALUE==hDir) return; BY_HANDLE_FILE_INFORMATION fi; GetFileInformationByHandle(hDir, &fi); CloseHandle(hDir); LARGE_INTEGER frn; frn.LowPart=fi.nFileIndexLow; frn.HighPart=fi.nFileIndexHigh; addFrn(ExtractFileName(root)+os_file_sep(), parent, frn.QuadPart, rid); std::vector<SFile> files=getFiles(root); for(size_t i=0;i<files.size();++i) { if(files[i].isdir) { indexRootDirs(rid, dir+files[i].name, frn.QuadPart); } } }
int zfs_test() { std::cout << "Testing for zfs..." << std::endl; if(getBackupfolderPath(mode_zfs).empty()) { std::cout << "TEST FAILED: Dataset is not set via /etc/urbackup/dataset" << std::endl; return 1; } std::string clientdir=getBackupfolderPath(mode_zfs)+os_file_sep()+"testA54hj5luZtlorr494"; if(create_subvolume(mode_zfs, clientdir) && remove_subvolume(mode_zfs, clientdir) ) { std::cout << "ZFS TEST OK" << std::endl; clientdir=getBackupfolderPath(mode_zfs_file)+os_file_sep()+"testA54hj5luZtlorr494"; if(getBackupfolderPath(mode_zfs_file).empty()) { return 10 + mode_zfs; } else if(create_subvolume(mode_zfs, clientdir) && remove_subvolume(mode_zfs, clientdir)) { return 10 + mode_zfs_file; } return 10 + mode_zfs; } else { std::cout << "TEST FAILED: Creating test zfs volume \"" << clientdir << "\" failed" << std::endl; } return 1; }
void BackupServer::operator()(void) { IDatabase *db=Server->getDatabase(Server->getThreadID(),URBACKUPDB_SERVER); ISettingsReader *settings=Server->createDBSettingsReader(Server->getDatabase(Server->getThreadID(),URBACKUPDB_SERVER), "settings_db.settings"); #ifdef _WIN32 std::wstring tmpdir; if(settings->getValue(L"tmpdir", &tmpdir) && !tmpdir.empty()) { os_remove_nonempty_dir(tmpdir+os_file_sep()+L"urbackup_tmp"); if(!os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp")) { Server->wait(5000); os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp"); } Server->setTemporaryDirectory(tmpdir+os_file_sep()+L"urbackup_tmp"); } else { wchar_t tmpp[MAX_PATH]; DWORD l; if((l=GetTempPathW(MAX_PATH, tmpp))==0 || l>MAX_PATH ) { wcscpy_s(tmpp,L"C:\\"); } std::wstring w_tmp=tmpp; if(!w_tmp.empty() && w_tmp[w_tmp.size()-1]=='\\') { w_tmp.erase(w_tmp.size()-1, 1); } os_remove_nonempty_dir(w_tmp+os_file_sep()+L"urbackup_tmp"); if(!os_create_dir(w_tmp+os_file_sep()+L"urbackup_tmp")) { Server->wait(5000); os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp"); } Server->setTemporaryDirectory(w_tmp+os_file_sep()+L"urbackup_tmp"); } #endif if( settings->getValue("use_tmpfiles", "")!="true" ) { std::wstring backupfolder; if( settings->getValue(L"backupfolder", &backupfolder) ) { std::wstring tmpfile_path=backupfolder+os_file_sep()+L"urbackup_tmp_files"; Server->Log("Removing temporary files..."); os_remove_nonempty_dir(tmpfile_path); Server->Log("Recreating temporary folder..."); if(!os_create_dir(tmpfile_path)) { Server->wait(5000); os_create_dir(tmpfile_path); } } } testSnapshotAvailability(db); q_get_extra_hostnames=db->Prepare("SELECT id,hostname FROM settings_db.extra_clients"); q_update_extra_ip=db->Prepare("UPDATE settings_db.extra_clients SET lastip=? WHERE id=?"); FileClient fc; Server->wait(1000); while(true) { findClients(fc); startClients(fc); if(!ServerStatus::isActive() && settings->getValue("autoshutdown", "false")=="true") { writestring("true", "urbackup/shutdown_now"); #ifdef _WIN32 ExitWindowsEx(EWX_POWEROFF|EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_OTHER ); #endif } std::string r; exitpipe->Read(&r, 20000); if(r=="exit") { removeAllClients(); exitpipe->Write("ok"); Server->destroy(settings); db->destroyAllQueries(); delete this; return; } } }
void FSNTFSWIN::logFileChangesInt(const std::string& volpath, int64 min_size, char* fc_bitmap, int64& total_files, int64& total_changed_sectors) { std::vector<SFile> files = getFiles(volpath); for (size_t i = 0; i < files.size(); ++i) { if (files[i].isdir) { if (!files[i].issym) { logFileChangesInt(volpath + os_file_sep() + files[i].name, min_size, fc_bitmap, total_files, total_changed_sectors); } } else if(!files[i].issym) { HANDLE hFile = CreateFileW(Server->ConvertToWchar(os_file_prefix(volpath + os_file_sep() + files[i].name)).c_str(), GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile != INVALID_HANDLE_VALUE) { int64 sector_count = 0; int64 total_sectors = 0; STARTING_VCN_INPUT_BUFFER start_vcn = {}; std::vector<char> ret_buf; ret_buf.resize(32768); while (true) { RETRIEVAL_POINTERS_BUFFER* ret_ptrs = reinterpret_cast<RETRIEVAL_POINTERS_BUFFER*>(ret_buf.data()); DWORD bytesRet = 0; BOOL b = DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &start_vcn, sizeof(start_vcn), ret_ptrs, static_cast<DWORD>(ret_buf.size()), &bytesRet, NULL); DWORD err = GetLastError(); if (b || err == ERROR_MORE_DATA) { LARGE_INTEGER last_vcn = ret_ptrs->StartingVcn; for (DWORD i = 0; i<ret_ptrs->ExtentCount; ++i) { if (ret_ptrs->Extents[i].Lcn.QuadPart != -1) { int64 count = ret_ptrs->Extents[i].NextVcn.QuadPart - last_vcn.QuadPart; sector_count += countSectors(ret_ptrs->Extents[i].Lcn.QuadPart, count, fc_bitmap); total_sectors += count; } last_vcn = ret_ptrs->Extents[i].NextVcn; } } if (!b) { if (err == ERROR_MORE_DATA) { start_vcn.StartingVcn = ret_ptrs->Extents[ret_ptrs->ExtentCount - 1].NextVcn; } else if (err == ERROR_HANDLE_EOF) { CloseHandle(hFile); break; } else { Server->Log("Error " + convert((int)GetLastError()) + " while accessing retrieval points", LL_WARNING); CloseHandle(hFile); break; } } else { CloseHandle(hFile); break; } } if (sector_count > 0) { ++total_files; total_changed_sectors += sector_count; } if (sector_count > 0 && sector_count*getBlocksize()>min_size) { Server->Log("Changed in " + volpath + os_file_sep() + files[i].name + ": " + convert(sector_count) + "/"+convert(total_sectors)+" sectors " + PrettyPrintBytes(sector_count*getBlocksize()) +"/"+ PrettyPrintBytes(total_sectors*getBlocksize()), LL_INFO); } } else { Server->Log("Error opening file: " + volpath + os_file_sep() + files[i].name, LL_INFO); } } } }
std::vector<SFile> getFilesWin(const std::string &path, bool *has_error, bool exact_filesize, bool with_usn, bool ignore_other_fs) { if(has_error!=NULL) { *has_error=false; } std::vector<char> usn_buffer; usn_buffer.resize(1024); std::vector<SFile> tmp; HANDLE fHandle; WIN32_FIND_DATAW wfd; std::wstring tpath=ConvertToWchar(path); if(!tpath.empty() && tpath[tpath.size()-1]=='\\' ) tpath.erase(tpath.size()-1, 1); fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); if(fHandle==INVALID_HANDLE_VALUE) { if(tpath.find(L"\\\\?\\UNC")==0) { tpath.erase(0, 7); tpath=L"\\"+tpath; fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); } else if(tpath.find(L"\\\\?\\")==0) { tpath.erase(0, 4); fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); } if(fHandle==INVALID_HANDLE_VALUE) { if(has_error!=NULL) { *has_error=true; } return tmp; } } do { SFile f; f.name=ConvertFromWchar(wfd.cFileName); if(f.name=="." || f.name==".." ) continue; f.usn=0; f.isdir=(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)>0; LARGE_INTEGER lwt; lwt.HighPart=wfd.ftLastWriteTime.dwHighDateTime; lwt.LowPart=wfd.ftLastWriteTime.dwLowDateTime; f.last_modified=os_windows_to_unix_time(lwt.QuadPart); LARGE_INTEGER size; size.HighPart=wfd.nFileSizeHigh; size.LowPart=wfd.nFileSizeLow; f.size=size.QuadPart; lwt.HighPart=wfd.ftCreationTime.dwHighDateTime; lwt.LowPart=wfd.ftCreationTime.dwLowDateTime; f.created=os_windows_to_unix_time(lwt.QuadPart); std::string reparse_target; if (ignore_other_fs && wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT && os_get_symlink_target(os_file_prefix(path + os_file_sep() + f.name), reparse_target) && is_volume(reparse_target) ) { continue; } else if(wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && (wfd.dwReserved0== IO_REPARSE_TAG_SYMLINK || wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) ) { f.issym=true; f.isspecialf=true; } else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && (wfd.dwReserved0 == 0x8000001b /*IO_REPARSE_TAG_APPEXECLINK*/ )) { f.isspecialf = true; } f.isencrypted = (wfd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)>0; if( (exact_filesize && !f.isdir && !f.issym ) || with_usn ) { if(with_usn) { HANDLE hFile = CreateFileW(os_file_prefix(tpath+L"\\"+ConvertToWchar(f.name)).c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if(hFile!=INVALID_HANDLE_VALUE) { if (exact_filesize && !f.isdir && !f.issym) { BY_HANDLE_FILE_INFORMATION fileInformation; if (GetFileInformationByHandle(hFile, &fileInformation)!=0) { LARGE_INTEGER fsize; fsize.LowPart = fileInformation.nFileSizeLow; fsize.HighPart = fileInformation.nFileSizeHigh; f.size = fsize.QuadPart; f.nlinks = fileInformation.nNumberOfLinks; } } DWORD last_err=0; do { DWORD ret_bytes = 0; BOOL b = DeviceIoControl(hFile, FSCTL_READ_FILE_USN_DATA, NULL, 0, usn_buffer.data(), static_cast<DWORD>(usn_buffer.size()), &ret_bytes, NULL); if(b) { USN_RECORD* usnv2=reinterpret_cast<USN_RECORD*>(usn_buffer.data()); if(usnv2->MajorVersion==2) { f.usn = usnv2->Usn; } else if(usnv2->MajorVersion==3) { usn::USN_RECORD_V3* usnv3=reinterpret_cast<usn::USN_RECORD_V3*>(usn_buffer.data()); f.usn = usnv3->Usn; } else { Log("USN entry major version "+convert(usnv2->MajorVersion)+" of file \""+ConvertFromWchar(tpath)+"\\"+f.name+"\" not supported", LL_ERROR); } } else { last_err=GetLastError(); } if(last_err==ERROR_INSUFFICIENT_BUFFER) { usn_buffer.resize(usn_buffer.size()*2); } } while (last_err==ERROR_INSUFFICIENT_BUFFER); CloseHandle(hFile); } } else { WIN32_FILE_ATTRIBUTE_DATA fad; if( GetFileAttributesExW(os_file_prefix(tpath+L"\\"+ConvertToWchar(f.name)).c_str(), GetFileExInfoStandard, &fad) ) { size.HighPart = fad.nFileSizeHigh; size.LowPart = fad.nFileSizeLow; f.size = size.QuadPart; lwt.HighPart = fad.ftLastWriteTime.dwHighDateTime; lwt.LowPart = fad.ftLastWriteTime.dwLowDateTime; f.last_modified = os_windows_to_unix_time(lwt.QuadPart); lwt.HighPart=fad.ftCreationTime.dwHighDateTime; lwt.LowPart=fad.ftCreationTime.dwLowDateTime; f.created=os_windows_to_unix_time(lwt.QuadPart); } } } if(f.last_modified<0) f.last_modified*=-1; tmp.push_back(f); } while (FindNextFileW(fHandle,&wfd) ); if (GetLastError() != ERROR_NO_MORE_FILES) { DWORD err = GetLastError(); if (has_error != NULL) { *has_error = true; } FindClose(fHandle); std::sort(tmp.begin(), tmp.end()); SetLastError(err); return tmp; } FindClose(fHandle); std::sort(tmp.begin(), tmp.end()); return tmp; }
bool os_sync(const std::string & path) { std::string prefixedbpath = os_file_prefix(path); if (next(prefixedbpath, 0, "\\\\?\\UNC")) { HANDLE hVol = CreateFileW(ConvertToWchar(prefixedbpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVol == INVALID_HANDLE_VALUE) { return false; } BOOL b = FlushFileBuffers(hVol); CloseHandle(hVol); return b == TRUE; } std::wstring tvolume; tvolume.resize(prefixedbpath.size() + 100); DWORD cchBufferLength = static_cast<DWORD>(tvolume.size()); BOOL b = GetVolumePathNameW(ConvertToWchar(prefixedbpath).c_str(), &tvolume[0], cchBufferLength); if (!b) { return false; } std::string volume = ConvertFromWchar(tvolume.c_str()); if (volume.find("\\\\?\\") == 0 && volume.find("\\\\?\\GLOBALROOT") != 0) { volume.erase(0, 4); } if (!volume.empty() && volume[volume.size() - 1] == os_file_sep()[0]) { volume = volume.substr(0, volume.size() - 1); } HANDLE hVol = CreateFileW(ConvertToWchar("\\\\.\\"+volume).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVol == INVALID_HANDLE_VALUE) { return false; } b = FlushFileBuffers(hVol); CloseHandle(hVol); return b == TRUE; }
void ChangeJournalWatcher::indexRootDirs2(const std::wstring &root, SChangeJournal *sj) { db->Write("CREATE TEMPORARY TABLE map_frn_tmp (name TEXT, pid INTEGER, frn INTEGER, rid INTEGER)"); q_add_frn_tmp=db->Prepare("INSERT INTO map_frn_tmp (name, pid, frn, rid) VALUES (?, ?, ?, ?)", false); HANDLE hDir = CreateFile((root+os_file_sep()).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hDir==INVALID_HANDLE_VALUE) { Server->Log("Could not open root HANDLE.", LL_ERROR); return; } BY_HANDLE_FILE_INFORMATION fi; GetFileInformationByHandle(hDir, &fi); CloseHandle(hDir); LARGE_INTEGER frn; frn.LowPart=fi.nFileIndexLow; frn.HighPart=fi.nFileIndexHigh; addFrn(root, -1, frn.QuadPart, sj->rid); MFT_ENUM_DATA med; med.StartFileReferenceNumber = 0; med.LowUsn = 0; med.HighUsn = MAXLONGLONG; //sj->last_record; // Process MFT in 64k chunks BYTE *pData=new BYTE[sizeof(DWORDLONG) + 0x10000]; DWORDLONG fnLast = 0; DWORD cb; while (DeviceIoControl(sj->hVolume, FSCTL_ENUM_USN_DATA, &med, sizeof(med),pData, sizeof(DWORDLONG) + 0x10000, &cb, NULL) != FALSE) { if(indexing_in_progress) { if(Server->getTimeMS()-last_index_update>10000) { update(false, sj->vol_str); last_index_update=Server->getTimeMS(); } } if(dwt->is_stopped()) { Server->Log("Stopped indexing process", LL_WARNING); break; } PUSN_RECORD pRecord = (PUSN_RECORD) &pData[sizeof(USN)]; while ((PBYTE) pRecord < (pData + cb)) { if((pRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0) { std::wstring filename; filename.resize(pRecord->FileNameLength / sizeof(wchar_t) ); memcpy(&filename[0], (PBYTE) pRecord + pRecord->FileNameOffset, pRecord->FileNameLength); addFrnTmp(filename, pRecord->ParentFileReferenceNumber, pRecord->FileReferenceNumber, sj->rid); } pRecord = (PUSN_RECORD) ((PBYTE) pRecord + pRecord->RecordLength); } med.StartFileReferenceNumber = * (DWORDLONG *) pData; } delete []pData; db->destroyQuery(q_add_frn_tmp); q_add_frn_tmp=NULL; db->Write("INSERT INTO map_frn (name, pid, frn, rid) SELECT name, pid, frn, rid FROM map_frn_tmp"); db->Write("DROP TABLE map_frn_tmp"); update(true, sj->vol_str); }
int main(int argc, char *argv[]) { if(argc<2) { std::cout << "Not enough parameters" << std::endl; return 1; } std::string cmd; int mode = 0; if((std::string)argv[1]!="test") { if(argc<3) { std::cout << "Not enough parameters" << std::endl; return 1; } cmd=argv[2]; mode=atoi(argv[1]); } else { cmd=argv[1]; } std::string backupfolder=getBackupfolderPath(mode); if(backupfolder.empty()) { if(mode==mode_btrfs) { std::cout << "Backupfolder not set" << std::endl; } else if(mode==mode_zfs) { std::cout << "ZFS image dataset not set" << std::endl; } else if(mode==mode_zfs_file) { std::cout << "ZFS file dataset not set" << std::endl; } else { std::cout << "Unknown mode: " << mode << std::endl; } return 1; } if(cmd!="test" && mode==mode_zfs_file) { mode=mode_zfs; } #ifndef _WIN32 if(seteuid(0)!=0) { std::cout << "Cannot become root user" << std::endl; return 1; } #endif if(cmd=="create") { if(argc<5) { std::cout << "Not enough parameters for create" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string name=handleFilename(argv[4]); std::string subvolume_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+name; return create_subvolume(mode, subvolume_folder)?0:1; } else if(cmd=="mountpoint") { if(argc<5) { std::cout << "Not enough parameters for mountpoint" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string name=handleFilename(argv[4]); std::string subvolume_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+name; return get_mountpoint(mode, subvolume_folder)?0:1; } else if(cmd=="snapshot") { if(argc<6) { std::cout << "Not enough parameters for snapshot" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string src_name=handleFilename(argv[4]); std::string dst_name=handleFilename(argv[5]); std::string subvolume_src_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+src_name; std::string subvolume_dst_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+dst_name; return create_snapshot(mode, subvolume_src_folder, subvolume_dst_folder)?0:1; } else if(cmd=="remove") { if(argc<5) { std::cout << "Not enough parameters for remove" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string name=handleFilename(argv[4]); std::string subvolume_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+name; return remove_subvolume(mode, subvolume_folder)?0:1; } else if(cmd=="test") { std::cout << "Testing for btrfs..." << std::endl; std::string clientdir=backupfolder+os_file_sep()+"testA54hj5luZtlorr494"; bool create_dir_rc=os_create_dir(clientdir); if(!create_dir_rc) { remove_subvolume(mode_zfs, clientdir, true); remove_subvolume(mode_btrfs, clientdir+os_file_sep()+"A", true); remove_subvolume(mode_btrfs, clientdir+os_file_sep()+"B", true); os_remove_dir(clientdir); } create_dir_rc = create_dir_rc || os_create_dir(clientdir); if(create_dir_rc) { if(!create_subvolume(mode_btrfs, clientdir+os_file_sep()+"A") ) { std::cout << "TEST FAILED: Creating test btrfs subvolume failed" << std::endl; os_remove_dir(clientdir); return zfs_test(); } bool suc=true; if(!create_snapshot(mode_btrfs, clientdir+os_file_sep()+"A", clientdir+os_file_sep()+"B") ) { std::cout << "TEST FAILED: Creating test snapshot failed" << std::endl; suc=false; } if(suc) { writestring("test", clientdir+os_file_sep()+"A"+os_file_sep()+"test"); if(!os_create_hardlink(clientdir+os_file_sep()+"B"+os_file_sep()+"test", clientdir+os_file_sep()+"A"+os_file_sep()+"test", true, NULL)) { std::cout << "TEST FAILED: Creating cross sub-volume reflink failed. Need Linux kernel >= 3.6." << std::endl; suc=false; } else { if(getFile(clientdir+os_file_sep()+"B"+os_file_sep()+"test")!="test") { std::cout << "TEST FAILED: Cannot read reflinked file" << std::endl; suc=false; } } } if(!remove_subvolume(mode_btrfs, clientdir+os_file_sep()+"A") ) { std::cout << "TEST FAILED: Removing subvolume A failed" << std::endl; suc=false; } if(!remove_subvolume(mode_btrfs, clientdir+os_file_sep()+"B") ) { std::cout << "TEST FAILED: Removing subvolume B failed" << std::endl; suc=false; } if(!os_remove_dir(clientdir)) { std::cout << "TEST FAILED: Removing test clientdir failed" << std::endl; return 1; } if(!suc) { return 1; } } else { std::cout << "TEST FAILED: Creating test clientdir \"" << clientdir << "\" failed" << std::endl; return zfs_test(); } std::cout << "BTRFS TEST OK" << std::endl; return 10 + mode_btrfs; } else if(cmd=="issubvolume") { if(argc<5) { std::cout << "Not enough parameters for issubvolume" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string name=handleFilename(argv[4]); std::string subvolume_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+name; return is_subvolume(mode, subvolume_folder)?0:1; } else if(cmd=="makereadonly") { if(argc<5) { std::cout << "Not enough parameters for makereadonly" << std::endl; return 1; } std::string clientname=handleFilename(argv[3]); std::string name=handleFilename(argv[4]); std::string subvolume_folder=backupfolder+os_file_sep()+clientname+os_file_sep()+name; return make_readonly(mode, subvolume_folder)?0:1; } else { std::cout << "Command not found" << std::endl; return 1; } }