Bumped copyright dates to 2012
[barry/progweb.git] / src / restore.cc
blob0f33a6629c75980bf915d483a8ff84ec1b613a1a
1 ///
2 /// \file restore.cc
3 /// Builder class for restoring from Barry Backup files
4 ///
6 /*
7 Copyright (C) 2010-2012, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "restore.h"
23 #include "tarfile.h"
24 #include "error.h"
25 #include <sstream>
26 #include <iomanip>
27 #include <iostream>
28 #include <string.h>
29 #include <algorithm>
31 using namespace std;
33 namespace Barry {
35 namespace {
37 int CountFiles(reuse::TarFile &tar,
38 const Barry::Restore::DBListType &restoreList,
39 bool default_all_db)
41 int count = 0;
42 std::string name, last_name;
43 bool good = false;
45 while( tar.ReadNextFilenameOnly(name) ) {
46 std::string::size_type pos = name.rfind('/');
47 if( pos == std::string::npos )
48 continue; // bad name
49 std::string dbname = name.substr(0, pos);
51 if( dbname != last_name ) {
52 last_name = dbname;
53 good = (default_all_db && restoreList.size() == 0) ||
54 restoreList.IsSelected(dbname);
56 if( good )
57 count++;
59 return count;
64 //////////////////////////////////////////////////////////////////////////////
65 // Static Restore members
67 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
68 /// Returns true if successful, false if tarpath is a bad name.
69 bool Restore::SplitTarPath(const std::string &tarpath,
70 std::string &dbname,
71 std::string &dbid_text,
72 uint8_t &dbrectype,
73 uint32_t &dbid)
75 std::string::size_type pos = tarpath.rfind('/');
76 if( pos == std::string::npos )
77 return false; // bad name
79 dbname = tarpath.substr(0, pos);
80 dbid_text = tarpath.substr(pos + 1);
81 if( dbname.size() == 0 || dbid_text.size() == 0 )
82 return false; // bad name
84 std::istringstream iss(dbid_text);
85 unsigned int temp;
86 iss >> std::hex >> dbid >> temp;
87 dbrectype = (uint8_t) temp;
89 return true;
93 //////////////////////////////////////////////////////////////////////////////
94 // Restore - constructors
96 Restore::Restore(const std::string &tarpath, bool default_all_db)
97 : m_tarpath(tarpath)
98 , m_default_all_db(default_all_db)
99 , m_tar_record_state(RS_EMPTY)
100 , m_rec_type(0)
101 , m_unique_id(0)
103 try {
104 m_tar.reset( new reuse::TarFile(tarpath.c_str(), false,
105 &reuse::gztar_ops_nonthread, true) );
107 catch( reuse::TarFile::TarError &te ) {
108 throw Barry::RestoreError(te.what());
112 Restore::~Restore()
117 //////////////////////////////////////////////////////////////////////////////
118 // Restore - Protected helpers
120 bool Restore::IsSelected(const std::string &dbName) const
122 // if nothing is in the list, use default
123 if( m_dbList.size() == 0 )
124 return m_default_all_db;
125 else
126 return m_dbList.IsSelected(dbName);
130 //////////////////////////////////////////////////////////////////////////////
131 // Restore - Public API
133 void Restore::AddDB(const std::string &dbName)
135 if( find(m_dbList.begin(), m_dbList.end(), dbName) == m_dbList.end() ) {
136 // only add it if it is not already in the list
137 m_dbList.push_back(dbName);
141 void Restore::Add(const DBListType &dbList)
143 for( DBListType::const_iterator i = dbList.begin();
144 i != dbList.end();
145 ++i )
147 AddDB(*i);
151 void Restore::SkipCurrentDB()
153 // skip all records until next DB
154 try {
155 while( Retrieve(m_record_data) == RS_NEXT ) {
156 std::cerr << "Skipping: "
157 << m_current_dbname << "/"
158 << m_tar_id_text << std::endl;
159 m_tar_record_state = RS_EMPTY;
162 catch( reuse::TarFile::TarError & ) {
163 m_tar_record_state = RS_EOF;
167 unsigned int Restore::GetRecordTotal() const
169 return GetRecordTotal(m_tarpath, m_dbList, m_default_all_db);
172 unsigned int Restore::GetRecordTotal(const std::string &tarpath,
173 const DBListType &dbList,
174 bool default_all_db)
176 unsigned int count = 0;
178 std::auto_ptr<reuse::TarFile> tar;
180 try {
181 // do a scan through the tar file
182 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
183 &reuse::gztar_ops_nonthread, true) );
184 count = CountFiles(*tar, dbList, default_all_db);
186 catch( reuse::TarFile::TarError &te ) {
187 throw Barry::RestoreError(te.what());
189 return count;
192 bool Restore::GetNextMeta(DBData &data)
194 // always use m_record_data here, so that we don't lose access
195 // to the actual record data for future calls to BuildRecord()
196 // and FetchRecord()
197 if( m_tar_record_state == RS_EMPTY ) {
198 Retrieve(m_record_data);
201 // fill in the meta data that will be returned in the next call
202 // to BuildRecord() or FetchRecord()... this is only valid if
203 // the state is RS_NEXT
204 switch( m_tar_record_state )
206 case RS_NEXT:
207 data.SetVersion(Barry::DBData::REC_VERSION_1);
208 data.SetDBName(m_current_dbname);
209 data.SetIds(m_rec_type, m_unique_id);
210 data.SetOffset(0);
211 return true;
213 default:
214 return false;
219 //////////////////////////////////////////////////////////////////////////////
220 // Barry::Builder overrides
222 Restore::RetrievalState Restore::Retrieve(Data &record_data)
224 // don't do anything unless we're empty
225 if( m_tar_record_state != RS_EMPTY )
226 return m_tar_record_state;
228 // search for a valid record
229 for(;;) {
230 // load record data from tar file
231 std::string filename;
232 if( !m_tar->ReadNextFile(filename, record_data) ) {
233 // assume end of file
234 return m_tar_record_state = RS_EOF;
236 m_tar_record_state = RS_UNKNOWN;
238 // split record filename into dbname and ID
239 std::string dbname;
240 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
241 // invalid filename, skip it
242 std::cerr << "Skipping invalid tar record: " << filename << std::endl;
243 continue;
246 // are we working on the same dbname as last time?
247 // if so, go ahead!
248 if( m_current_dbname == dbname ) {
249 return m_tar_record_state = RS_NEXT;
252 // DIFFERENT DBNAME from here on down!
253 m_tar_record_state = RS_DBEND;
255 // does the filter allow this record?
256 // if not, skip it and continue looking
257 if( !IsSelected(dbname) ) {
258 continue;
261 // all checks pass, load the new dbname, and return DBEND
262 // if we are on a dbname boundary
263 if( m_current_dbname.size() == 0 ) {
264 // this is the first time through Retrieve, so ok
265 m_tar_record_state = RS_NEXT;
268 m_current_dbname = dbname;
269 return m_tar_record_state;
273 bool Restore::BuildRecord(Barry::DBData &data,
274 size_t &offset,
275 const Barry::IConverter *ic)
277 // in this case, we are loading into m_record_data anyway,
278 // so no special handling is needed, like FetchRecord() needs.
279 switch( Retrieve(m_record_data) )
281 case RS_NEXT:
283 data.SetVersion(Barry::DBData::REC_VERSION_1);
284 data.SetDBName(m_current_dbname);
285 data.SetIds(m_rec_type, m_unique_id);
286 data.SetOffset(offset);
288 int packet_size = offset + m_record_data.GetSize();
289 unsigned char *buf = data.UseData().GetBuffer(packet_size);
290 memcpy(buf + offset, m_record_data.GetData(), m_record_data.GetSize());
291 offset += m_record_data.GetSize();
292 data.UseData().ReleaseBuffer(packet_size);
294 // clear loaded flag, as it has now been used
295 m_tar_record_state = RS_EMPTY;
296 return true;
299 case RS_EMPTY:
300 case RS_UNKNOWN:
301 default:
302 throw std::logic_error("Invalid state in Restore::BuildRecord()");
304 case RS_DBEND:
305 // process the end of database by returning false
306 // the next round will be valid, so set to RS_NEXT
307 m_tar_record_state = RS_NEXT;
308 return false;
310 case RS_EOF:
311 // always return false at end of file
312 return false;
316 bool Restore::FetchRecord(Barry::DBData &data, const Barry::IConverter *ic)
318 // if the record has not yet been loaded, we can optimize
319 // the buffer, and pass in our own... otherwise, just copy
320 // the current buffer from m_record_data
322 // it is assumed here that Builder users will not alternate
323 // between calls to BuildRecord() and FetchRecord()
325 if( m_tar_record_state == RS_EMPTY ) {
326 // BUT, if RS_DBEND is the next value, then we need
327 // to save the data for the next round... this
328 // optimization is almost more bother than it's worth :-)
329 if( Retrieve(data.UseData()) == RS_DBEND ) {
330 m_record_data = data.GetData();
331 m_tar_record_state = RS_NEXT;
332 return false;
335 else {
336 data.UseData() = m_record_data;
339 switch( m_tar_record_state )
341 case RS_NEXT:
342 data.SetVersion(Barry::DBData::REC_VERSION_1);
343 data.SetDBName(m_current_dbname);
344 data.SetIds(m_rec_type, m_unique_id);
345 data.SetOffset(0);
347 // clear loaded flag, as it has now been used
348 m_tar_record_state = RS_EMPTY;
349 return true;
351 case RS_EMPTY:
352 case RS_UNKNOWN:
353 default:
354 throw std::logic_error("Invalid state in Restore::FetchRecord()");
356 case RS_DBEND:
357 // process the end of database by returning false
358 // the next round will be valid, so set to RS_NEXT
359 m_tar_record_state = RS_NEXT;
360 return false;
362 case RS_EOF:
363 // always return false at end of file
364 return false;
368 bool Restore::EndOfFile() const
370 return m_tar_record_state == RS_EOF;
373 } // namespace Barry