lib: added convenience Add function to Restore that takes a DBList
[barry.git] / src / restore.cc
blob242e1888d72b197a077d6b9d480853845a5464b0
1 ///
2 /// \file restore.cc
3 /// Builder class for restoring from Barry Backup files
4 ///
6 /*
7 Copyright (C) 2010, 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_end_of_tar(false)
100 , m_tar_record_loaded(false)
101 , m_rec_type(0)
102 , m_unique_id(0)
104 try {
105 m_tar.reset( new reuse::TarFile(tarpath.c_str(), false,
106 &reuse::gztar_ops_nonthread, true) );
108 catch( reuse::TarFile::TarError &te ) {
109 throw Barry::RestoreError(te.what());
113 Restore::~Restore()
118 //////////////////////////////////////////////////////////////////////////////
119 // Restore - Protected helpers
121 bool Restore::IsSelected(const std::string &dbName) const
123 // if nothing is in the list, use default
124 if( m_dbList.size() == 0 )
125 return m_default_all_db;
126 else
127 return m_dbList.IsSelected(dbName);
131 //////////////////////////////////////////////////////////////////////////////
132 // Restore - Public API
134 void Restore::AddDB(const std::string &dbName)
136 if( find(m_dbList.begin(), m_dbList.end(), dbName) == m_dbList.end() ) {
137 // only add it if it is not already in the list
138 m_dbList.push_back(dbName);
142 void Restore::Add(const DBListType &dbList)
144 for( DBListType::const_iterator i = dbList.begin();
145 i != dbList.end();
146 ++i )
148 AddDB(*i);
152 void Restore::SkipCurrentDB()
154 // skip all records until next DB
155 try {
156 while( Retrieve(m_record_data) ) {
157 std::cerr << "Skipping: "
158 << m_current_dbname << "/"
159 << m_tar_id_text << std::endl;
160 m_tar_record_loaded = false;
163 catch( reuse::TarFile::TarError & ) {
164 m_end_of_tar = true;
168 unsigned int Restore::GetRecordTotal() const
170 return GetRecordTotal(m_tarpath, m_dbList, m_default_all_db);
173 unsigned int Restore::GetRecordTotal(const std::string &tarpath,
174 const DBListType &dbList,
175 bool default_all_db)
177 unsigned int count = 0;
179 std::auto_ptr<reuse::TarFile> tar;
181 try {
182 // do a scan through the tar file
183 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
184 &reuse::gztar_ops_nonthread, true) );
185 count = CountFiles(*tar, dbList, default_all_db);
187 catch( reuse::TarFile::TarError &te ) {
188 throw Barry::RestoreError(te.what());
190 return count;
194 //////////////////////////////////////////////////////////////////////////////
195 // Barry::Builder overrides
197 bool Restore::Retrieve(Data &record_data)
199 if( m_end_of_tar )
200 return false;
202 // if loaded, we are likely on a database
203 // boundary, and the last read crossed it, so don't load again
204 if( m_tar_record_loaded )
205 return true;
207 // search for a valid record
208 for(;;) {
209 // load record data from tar file
210 std::string filename;
211 if( !m_tar->ReadNextFile(filename, record_data) ) {
212 // assume end of file
213 m_end_of_tar = true;
214 return false;
216 m_tar_record_loaded = true;
218 // split record filename into dbname and ID
219 std::string dbname;
220 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
221 // invalid filename, skip it
222 std::cerr << "Skipping invalid tar record: " << filename << std::endl;
223 continue;
226 // are we working on the same dbname as last time?
227 // if so, go ahead!
228 if( m_current_dbname == dbname ) {
229 return true;
232 // DIFFERENT DBNAME from here on down!
234 // does the filter allow this record?
235 // if not, skip it and continue looking
236 if( !IsSelected(dbname) ) {
237 m_tar_record_loaded = false;
238 continue;
241 // all checks pass, load the new dbname, and return false
242 // if we are on a dbname boundary
243 bool r_val = false;
244 if( m_current_dbname.size() == 0 ) {
245 // this is the first time through Retrieve, so ok
246 r_val = true;
249 m_current_dbname = dbname;
250 return r_val;
254 bool Restore::BuildRecord(Barry::DBData &data,
255 size_t &offset,
256 const Barry::IConverter *ic)
258 if( !Retrieve(m_record_data) )
259 return false;
261 data.SetVersion(Barry::DBData::REC_VERSION_1);
262 data.SetDBName(m_current_dbname);
263 data.SetIds(m_rec_type, m_unique_id);
264 data.SetOffset(offset);
266 int packet_size = offset + m_record_data.GetSize();
267 unsigned char *buf = data.UseData().GetBuffer(packet_size);
268 memcpy(buf + offset, m_record_data.GetData(), m_record_data.GetSize());
269 offset += m_record_data.GetSize();
270 data.UseData().ReleaseBuffer(packet_size);
272 // clear loaded flag, as it has now been used
273 m_tar_record_loaded = false;
274 return true;
277 bool Restore::FetchRecord(Barry::DBData &data, const Barry::IConverter *ic)
279 if( !Retrieve(data.UseData()) )
280 return false;
282 data.SetVersion(Barry::DBData::REC_VERSION_1);
283 data.SetDBName(m_current_dbname);
284 data.SetIds(m_rec_type, m_unique_id);
285 data.SetOffset(0);
287 // clear loaded flag, as it has now been used
288 m_tar_record_loaded = false;
289 return true;
292 bool Restore::EndOfFile() const
294 return m_end_of_tar;
297 } // namespace Barry