3 /// Builder class for restoring from Barry Backup files
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.
37 int CountFiles(reuse::TarFile
&tar
,
38 const Barry::Restore::DBListType
&restoreList
,
39 Barry::Restore::DBListType
*available
,
43 std::string name
, last_name
;
46 while( tar
.ReadNextFilenameOnly(name
) ) {
47 std::string::size_type pos
= name
.rfind('/');
48 if( pos
== std::string::npos
)
50 std::string dbname
= name
.substr(0, pos
);
52 if( dbname
!= last_name
) {
54 good
= (default_all_db
&& restoreList
.size() == 0) ||
55 restoreList
.IsSelected(dbname
);
57 if( good
&& available
)
58 available
->push_back(dbname
);
68 //////////////////////////////////////////////////////////////////////////////
69 // Static Restore members
71 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
72 /// Returns true if successful, false if tarpath is a bad name.
73 bool Restore::SplitTarPath(const std::string
&tarpath
,
75 std::string
&dbid_text
,
79 std::string::size_type pos
= tarpath
.rfind('/');
80 if( pos
== std::string::npos
)
81 return false; // bad name
83 dbname
= tarpath
.substr(0, pos
);
84 dbid_text
= tarpath
.substr(pos
+ 1);
85 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
86 return false; // bad name
88 std::istringstream
iss(dbid_text
);
90 iss
>> std::hex
>> dbid
>> temp
;
91 dbrectype
= (uint8_t) temp
;
97 //////////////////////////////////////////////////////////////////////////////
98 // Restore - constructors
100 Restore::Restore(const std::string
&tarpath
, bool default_all_db
)
102 , m_default_all_db(default_all_db
)
103 , m_tar_record_state(RS_EMPTY
)
108 m_tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
109 &reuse::gztar_ops_nonthread
, true) );
111 catch( reuse::TarFile::TarError
&te
) {
112 throw Barry::RestoreError(te
.what());
121 //////////////////////////////////////////////////////////////////////////////
122 // Restore - Protected helpers
124 bool Restore::IsSelected(const std::string
&dbName
) const
126 // if in skip list, always return false,
127 // if nothing is in the main list, use default
128 // otherwise, only return true if specifically selected
129 if( m_dbSkipList
.IsSelected(dbName
) )
131 else if( m_dbList
.size() == 0 )
132 return m_default_all_db
;
134 return m_dbList
.IsSelected(dbName
);
138 //////////////////////////////////////////////////////////////////////////////
139 // Restore - Public API
141 void Restore::AddDB(const std::string
&dbName
)
143 if( find(m_dbList
.begin(), m_dbList
.end(), dbName
) == m_dbList
.end() ) {
144 // only add it if it is not already in the list
145 m_dbList
.push_back(dbName
);
149 void Restore::Add(const DBListType
&dbList
)
151 for( DBListType::const_iterator i
= dbList
.begin();
159 void Restore::Add(const DatabaseDatabase
&dbdb
)
161 for( DatabaseDatabase::DatabaseArrayType::const_iterator i
= dbdb
.Databases
.begin();
162 i
!= dbdb
.Databases
.end();
169 void Restore::AddSkipDB(const std::string
&dbName
)
171 if( find(m_dbSkipList
.begin(), m_dbSkipList
.end(), dbName
) == m_dbSkipList
.end() ) {
172 // only add it if it is not already in the list
173 m_dbSkipList
.push_back(dbName
);
177 void Restore::SkipCurrentDB()
179 // skip all records until next DB
181 Restore::RetrievalState state
;
182 while( (state
= Retrieve(m_record_data
)) == RS_NEXT
) {
183 std::cerr
<< "Skipping: "
184 << m_current_dbname
<< "/"
185 << m_tar_id_text
<< std::endl
;
186 m_tar_record_state
= RS_EMPTY
;
189 if( state
== RS_DBEND
) {
190 // process the end of database, so that user is free
191 // to call GetNextMeta() or Retrieve() or BuildRecord()
192 m_tar_record_state
= RS_NEXT
;
195 catch( reuse::TarFile::TarError
& ) {
196 m_tar_record_state
= RS_EOF
;
200 unsigned int Restore::GetRecordTotal() const
202 return GetRecordTotal(m_tarpath
, m_dbList
, m_default_all_db
);
205 unsigned int Restore::GetRecordTotal(const std::string
&tarpath
,
206 const DBListType
&dbList
,
209 unsigned int count
= 0;
211 std::auto_ptr
<reuse::TarFile
> tar
;
214 // do a scan through the tar file
215 tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
216 &reuse::gztar_ops_nonthread
, true) );
217 count
= CountFiles(*tar
, dbList
, 0, default_all_db
);
219 catch( reuse::TarFile::TarError
&te
) {
220 throw Barry::RestoreError(te
.what());
225 Barry::Restore::DBListType
Restore::GetDBList() const
227 return GetDBList(m_tarpath
);
230 Barry::Restore::DBListType
Restore::GetDBList(const std::string
&tarpath
)
232 unsigned int count
= 0;
234 std::auto_ptr
<reuse::TarFile
> tar
;
235 DBListType available
, empty
;
238 // do a scan through the tar file
239 tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
240 &reuse::gztar_ops_nonthread
, true) );
241 count
= CountFiles(*tar
, empty
, &available
, true);
244 catch( reuse::TarFile::TarError
&te
) {
245 throw Barry::RestoreError(te
.what());
249 bool Restore::GetNextMeta(DBData
&data
)
251 // always use m_record_data here, so that we don't lose access
252 // to the actual record data for future calls to BuildRecord()
254 if( m_tar_record_state
== RS_EMPTY
) {
255 Retrieve(m_record_data
);
258 // fill in the meta data that will be returned in the next call
259 // to BuildRecord() or FetchRecord()... this is only valid if
260 // the state is RS_NEXT
261 switch( m_tar_record_state
)
264 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
265 data
.SetDBName(m_current_dbname
);
266 data
.SetIds(m_rec_type
, m_unique_id
);
276 //////////////////////////////////////////////////////////////////////////////
277 // Barry::Builder overrides
279 Restore::RetrievalState
Restore::Retrieve(Data
&record_data
)
281 // don't do anything unless we're empty
282 if( m_tar_record_state
!= RS_EMPTY
)
283 return m_tar_record_state
;
285 // search for a valid record
287 // load record data from tar file
288 std::string filename
;
289 if( !m_tar
->ReadNextFile(filename
, record_data
) ) {
290 // assume end of file
291 return m_tar_record_state
= RS_EOF
;
293 m_tar_record_state
= RS_UNKNOWN
;
295 // split record filename into dbname and ID
297 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
298 // invalid filename, skip it
299 std::cerr
<< "Skipping invalid tar record: " << filename
<< std::endl
;
303 // are we working on the same dbname as last time?
305 if( m_current_dbname
== dbname
) {
306 return m_tar_record_state
= RS_NEXT
;
309 // DIFFERENT DBNAME from here on down!
310 m_tar_record_state
= RS_DBEND
;
312 // does the filter allow this record?
313 // if not, skip it and continue looking
314 if( !IsSelected(dbname
) ) {
318 // all checks pass, load the new dbname, and return DBEND
319 // if we are on a dbname boundary
320 if( m_current_dbname
.size() == 0 ) {
321 // this is the first time through Retrieve, so ok
322 m_tar_record_state
= RS_NEXT
;
325 m_current_dbname
= dbname
;
326 return m_tar_record_state
;
330 bool Restore::BuildRecord(Barry::DBData
&data
,
332 const Barry::IConverter
*ic
)
334 // in this case, we are loading into m_record_data anyway,
335 // so no special handling is needed, like FetchRecord() needs.
336 switch( Retrieve(m_record_data
) )
340 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
341 data
.SetDBName(m_current_dbname
);
342 data
.SetIds(m_rec_type
, m_unique_id
);
343 data
.SetOffset(offset
);
345 int packet_size
= offset
+ m_record_data
.GetSize();
346 unsigned char *buf
= data
.UseData().GetBuffer(packet_size
);
347 memcpy(buf
+ offset
, m_record_data
.GetData(), m_record_data
.GetSize());
348 offset
+= m_record_data
.GetSize();
349 data
.UseData().ReleaseBuffer(packet_size
);
351 // clear loaded flag, as it has now been used
352 m_tar_record_state
= RS_EMPTY
;
359 throw std::logic_error("Invalid state in Restore::BuildRecord()");
362 // process the end of database by returning false
363 // the next round will be valid, so set to RS_NEXT
364 m_tar_record_state
= RS_NEXT
;
368 // always return false at end of file
373 bool Restore::FetchRecord(Barry::DBData
&data
, const Barry::IConverter
*ic
)
375 // if the record has not yet been loaded, we can optimize
376 // the buffer, and pass in our own... otherwise, just copy
377 // the current buffer from m_record_data
379 // it is assumed here that Builder users will not alternate
380 // between calls to BuildRecord() and FetchRecord()
382 if( m_tar_record_state
== RS_EMPTY
) {
383 // BUT, if RS_DBEND is the next value, then we need
384 // to save the data for the next round... this
385 // optimization is almost more bother than it's worth :-)
386 if( Retrieve(data
.UseData()) == RS_DBEND
) {
387 m_record_data
= data
.GetData();
388 m_tar_record_state
= RS_NEXT
;
393 data
.UseData() = m_record_data
;
396 switch( m_tar_record_state
)
399 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
400 data
.SetDBName(m_current_dbname
);
401 data
.SetIds(m_rec_type
, m_unique_id
);
404 // clear loaded flag, as it has now been used
405 m_tar_record_state
= RS_EMPTY
;
411 throw std::logic_error("Invalid state in Restore::FetchRecord()");
414 // process the end of database by returning false
415 // the next round will be valid, so set to RS_NEXT
416 m_tar_record_state
= RS_NEXT
;
420 // always return false at end of file
425 bool Restore::EndOfFile() const
427 return m_tar_record_state
== RS_EOF
;