urbackup_backend/urbackupserver/treediff/TreeReader.cpp
2021-03-31 19:42:58 +02:00

377 lines
7.6 KiB
C++

/*************************************************************************
* UrBackup - Client/Server backup system
* Copyright (C) 2011-2016 Martin Raiber
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include "TreeReader.h"
#include <fstream>
#include <iostream>
#include <memory.h>
#include <stack>
#include "../../stringtools.h"
#include "../../urbackupcommon/os_functions.h"
#include "../../Interface/Server.h"
#include "../../Interface/File.h"
#include <assert.h>
bool TreeReader::readTree(IFile* f)
{
f->Seek(0);
std::vector<char> buffer(512 * 1024);
size_t read;
int state=0;
size_t lines=0;
size_t stringbuffer_size=0;
std::string name;
char ltype=0;
do
{
bool has_read_error = false;
read = f->Read(buffer.data(), buffer.size(), &has_read_error);
if (has_read_error)
{
Log("Error reading from tree file -1");
return false;
}
for(size_t i=0;i<read;++i)
{
const char ch=buffer[i];
switch(state)
{
case 0:
if(ch=='f' || ch=='d')
{
ltype = ch;
state = 1;
}
else if(ch=='u')
{
state = 10;
ltype = 'd';
name = "..";
}
else
{
Log("Error parsing file readTree - 0. Expected 'f', 'd', or 'u'. Got '"+std::string(1, ch)+"' at line "+convert(lines)+" while reading "+ f->getFilename());
return false;
}
break;
case 1:
//"
state=2;
break;
case 2:
if(ch=='"')
{
state=3;
}
else if(ch=='\\')
{
state=5;
}
else
{
name+=ch;
}
break;
case 5:
if(ch!='\"' && ch!='\\')
{
name+='\\';
}
name+=ch;
state=2;
break;
case 3:
if(ch==' ')
{
state=4;
break;
}
else
{
state=10;
}
case 4:
if(state==4)
{
if(ch!='\n')
{
break;
}
}
case 10:
if(ch=='\n')
{
state=0;
if(ltype=='f')
{
stringbuffer_size+=2*sizeof(int64);
}
if(ltype!='d' || name!="..")
{
if(ltype=='d')
{
stringbuffer_size+=sizeof(int64);
}
++lines;
stringbuffer_size+=name.size()+1;
}
name.clear();
ltype=0;
}
}
}
}
while(read>0);
name.clear();
f->Seek(0);
size_t stringbuffer_pos=0;
stringbuffer.resize(stringbuffer_size+5);
state=0;
bool isdir=false;
std::string data;
std::stack<TreeNode*> parents;
std::stack<TreeNode*> lastNodes;
bool firstChild=true;
size_t idx=1;
nodes.resize(lines+1);
std::string root_str = "root";
memcpy(&stringbuffer[0], root_str.c_str(), root_str.size()+1);
stringbuffer_pos+=root_str.size()+1;
nodes[0].setName(&stringbuffer[0]);
parents.push(&nodes[0]);
lastNodes.push(&nodes[0]);
lines=0;
do
{
bool has_read_error = false;
read = f->Read(buffer.data(), buffer.size(), &has_read_error);
if (has_read_error)
{
Log("Error reading from tree file -2");
return false;
}
for(size_t i=0;i<read;++i)
{
const char ch=buffer[i];
switch(state)
{
case 0:
if(ch=='f')
{
data+='f';
state=1;
}
else if(ch=='d')
{
data+='d';
state=1;
}
else if(ch=='u')
{
name="..";
state=10;
}
else
{
Log("Error parsing file readTree - 1. Expected 'f', 'd', or 'u'. Got '" + std::string(1, ch) + "' at line " + convert(lines)+" while reading "+f->getFilename());
return false;
}
break;
case 1:
//"
state=2;
break;
case 2:
if(ch=='"')
{
state=3;
}
else if(ch=='\\')
{
state=5;
}
else
{
name+=ch;
}
break;
case 5:
if(ch!='\"' && ch!='\\')
{
name+='\\';
}
name+=ch;
state=2;
break;
case 3:
if(ch==' ')
{
state=4;
break;
}
else
{
state=10;
}
case 4:
if(state==4)
{
if(ch!='\n')
{
data+=ch;
break;
}
}
case 10:
if(ch=='\n')
{
if(name!="..")
{
memcpy(&stringbuffer[stringbuffer_pos], name.c_str(), name.size()+1);
nodes[idx].setName(&stringbuffer[stringbuffer_pos]);
stringbuffer_pos+=name.size()+1;
nodes[idx].setId(lines);
char ch=data[0];
nodes[idx].setType(ch);
if(ch=='f')
{
std::string sdata=data.substr(1);
std::string filesize=getuntil(" ", sdata);
std::string last_mod=getafter(" ", sdata);
_i64 ifilesize=os_atoi64(filesize);
_i64 ilast_mod=os_atoi64(last_mod);
char* ndata=&stringbuffer[stringbuffer_pos];
memcpy(&stringbuffer[stringbuffer_pos], &ifilesize, sizeof(_i64));
stringbuffer_pos+=sizeof(_i64);
memcpy(&stringbuffer[stringbuffer_pos], &ilast_mod, sizeof(_i64));
stringbuffer_pos+=sizeof(_i64);
nodes[idx].setData(ndata);
}
else
{
std::string sdata=data.substr(1);
std::string last_mod=getafter(" ", sdata);
_i64 ilast_mod=os_atoi64(last_mod);
char* ndata=&stringbuffer[stringbuffer_pos];
memcpy(&stringbuffer[stringbuffer_pos], &ilast_mod, sizeof(_i64));
stringbuffer_pos+=sizeof(_i64);
nodes[idx].setData(ndata);
}
if(firstChild)
{
lastNodes.push(&nodes[idx]);
firstChild=false;
}
else
{
lastNodes.top()->setNextSibling(&nodes[idx]);
lastNodes.pop();
lastNodes.push(&nodes[idx]);
}
if(!parents.empty())
{
parents.top()->incrementNumChildren();
nodes[idx].setParent(parents.top());
}
if(ch=='d')
{
parents.push(&nodes[idx]);
firstChild=true;
}
++idx;
}
else
{
if(!parents.empty())
{
parents.pop();
}
else
{
Log("TreeReader: parents empty");
return false;
}
if(!firstChild)
{
if(lastNodes.empty())
{
Log("TreeReader: lastNodes empty");
return false;
}
lastNodes.top()->setNextSibling(NULL);
lastNodes.pop();
}
firstChild=false;
}
name.clear();
data.clear();
state=0;
++lines;
}
}
}
}
while(read==buffer.size());
assert(idx == nodes.size());
assert(stringbuffer_pos == stringbuffer.size());
nodes[0].setNextSibling(NULL);
return true;
}
void TreeReader::Log(const std::string &str)
{
Server->Log(str, LL_ERROR);
}
std::vector<TreeNode> * TreeReader::getNodes(void)
{
return &nodes;
}