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.
38 int CountFiles(reuse::TarFile
&tar
,
39 const Barry::Restore::DBListType
&restoreList
,
40 Barry::Restore::DBListType
*available
,
44 std::string name
, last_name
;
47 while( tar
.ReadNextFilenameOnly(name
) ) {
48 std::string::size_type pos
= name
.rfind('/');
49 if( pos
== std::string::npos
)
51 std::string dbname
= name
.substr(0, pos
);
53 if( dbname
!= last_name
) {
55 good
= (default_all_db
&& restoreList
.size() == 0) ||
56 restoreList
.IsSelected(dbname
);
58 if( good
&& available
)
59 available
->push_back(dbname
);
69 //////////////////////////////////////////////////////////////////////////////
70 // Static Restore members
72 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
73 /// Returns true if successful, false if tarpath is a bad name.
74 bool Restore::SplitTarPath(const std::string
&tarpath
,
76 std::string
&dbid_text
,
80 std::string::size_type pos
= tarpath
.rfind('/');
81 if( pos
== std::string::npos
)
82 return false; // bad name
84 dbname
= tarpath
.substr(0, pos
);
85 dbid_text
= tarpath
.substr(pos
+ 1);
86 if( dbname
.size() == 0 || dbid_text
.size() == 0 )
87 return false; // bad name
89 std::istringstream
iss(dbid_text
);
91 iss
>> std::hex
>> dbid
>> temp
;
92 dbrectype
= (uint8_t) temp
;
98 //////////////////////////////////////////////////////////////////////////////
99 // Restore - constructors
101 Restore::Restore(const std::string
&tarpath
, bool default_all_db
)
103 , m_default_all_db(default_all_db
)
104 , m_tar_record_state(RS_EMPTY
)
109 m_tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
110 &reuse::gztar_ops_nonthread
, true) );
112 catch( reuse::TarFile::TarError
&te
) {
113 throw Barry::RestoreError(te
.what());
122 //////////////////////////////////////////////////////////////////////////////
123 // Restore - Protected helpers
125 bool Restore::IsSelected(const std::string
&dbName
) const
127 // if in skip list, always return false,
128 // if nothing is in the main list, use default
129 // otherwise, only return true if specifically selected
130 if( m_dbSkipList
.IsSelected(dbName
) )
132 else if( m_dbList
.size() == 0 )
133 return m_default_all_db
;
135 return m_dbList
.IsSelected(dbName
);
139 //////////////////////////////////////////////////////////////////////////////
140 // Restore - Public API
142 void Restore::AddDB(const std::string
&dbName
)
144 if( find(m_dbList
.begin(), m_dbList
.end(), dbName
) == m_dbList
.end() ) {
145 // only add it if it is not already in the list
146 m_dbList
.push_back(dbName
);
150 void Restore::Add(const DBListType
&dbList
)
152 for( DBListType::const_iterator i
= dbList
.begin();
160 void Restore::Add(const DatabaseDatabase
&dbdb
)
162 for( DatabaseDatabase::DatabaseArrayType::const_iterator i
= dbdb
.Databases
.begin();
163 i
!= dbdb
.Databases
.end();
170 void Restore::AddSkipDB(const std::string
&dbName
)
172 if( find(m_dbSkipList
.begin(), m_dbSkipList
.end(), dbName
) == m_dbSkipList
.end() ) {
173 // only add it if it is not already in the list
174 m_dbSkipList
.push_back(dbName
);
178 void Restore::SkipCurrentDB()
180 // skip all records until next DB
182 Restore::RetrievalState state
;
183 while( (state
= Retrieve(m_record_data
)) == RS_NEXT
) {
184 std::cerr
<< _("Skipping: ")
185 << m_current_dbname
<< "/"
186 << m_tar_id_text
<< std::endl
;
187 m_tar_record_state
= RS_EMPTY
;
190 if( state
== RS_DBEND
) {
191 // process the end of database, so that user is free
192 // to call GetNextMeta() or Retrieve() or BuildRecord()
193 m_tar_record_state
= RS_NEXT
;
196 catch( reuse::TarFile::TarError
& ) {
197 m_tar_record_state
= RS_EOF
;
201 unsigned int Restore::GetRecordTotal() const
203 return GetRecordTotal(m_tarpath
, m_dbList
, m_default_all_db
);
206 unsigned int Restore::GetRecordTotal(const std::string
&tarpath
,
207 const DBListType
&dbList
,
210 unsigned int count
= 0;
212 std::auto_ptr
<reuse::TarFile
> tar
;
215 // do a scan through the tar file
216 tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
217 &reuse::gztar_ops_nonthread
, true) );
218 count
= CountFiles(*tar
, dbList
, 0, default_all_db
);
220 catch( reuse::TarFile::TarError
&te
) {
221 throw Barry::RestoreError(te
.what());
226 Barry::Restore::DBListType
Restore::GetDBList() const
228 return GetDBList(m_tarpath
);
231 Barry::Restore::DBListType
Restore::GetDBList(const std::string
&tarpath
)
233 std::auto_ptr
<reuse::TarFile
> tar
;
234 DBListType available
, empty
;
237 // do a scan through the tar file
238 tar
.reset( new reuse::TarFile(tarpath
.c_str(), false,
239 &reuse::gztar_ops_nonthread
, true) );
240 CountFiles(*tar
, empty
, &available
, true);
243 catch( reuse::TarFile::TarError
&te
) {
244 throw Barry::RestoreError(te
.what());
248 bool Restore::GetNextMeta(DBData
&data
)
250 // always use m_record_data here, so that we don't lose access
251 // to the actual record data for future calls to BuildRecord()
253 if( m_tar_record_state
== RS_EMPTY
) {
254 Retrieve(m_record_data
);
257 // fill in the meta data that will be returned in the next call
258 // to BuildRecord() or FetchRecord()... this is only valid if
259 // the state is RS_NEXT
260 switch( m_tar_record_state
)
263 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
264 data
.SetDBName(m_current_dbname
);
265 data
.SetIds(m_rec_type
, m_unique_id
);
275 //////////////////////////////////////////////////////////////////////////////
276 // Barry::Builder overrides
278 Restore::RetrievalState
Restore::Retrieve(Data
&record_data
)
280 // don't do anything unless we're empty
281 if( m_tar_record_state
!= RS_EMPTY
)
282 return m_tar_record_state
;
284 // search for a valid record
286 // load record data from tar file
287 std::string filename
;
288 if( !m_tar
->ReadNextFile(filename
, record_data
) ) {
289 // assume end of file
290 return m_tar_record_state
= RS_EOF
;
292 m_tar_record_state
= RS_UNKNOWN
;
294 // split record filename into dbname and ID
296 if( !SplitTarPath(filename
, dbname
, m_tar_id_text
, m_rec_type
, m_unique_id
) ) {
297 // invalid filename, skip it
298 std::cerr
<< _("Skipping invalid tar record: ") << filename
<< std::endl
;
302 // are we working on the same dbname as last time?
304 if( m_current_dbname
== dbname
) {
305 return m_tar_record_state
= RS_NEXT
;
308 // DIFFERENT DBNAME from here on down!
309 m_tar_record_state
= RS_DBEND
;
311 // does the filter allow this record?
312 // if not, skip it and continue looking
313 if( !IsSelected(dbname
) ) {
317 // all checks pass, load the new dbname, and return DBEND
318 // if we are on a dbname boundary
319 if( m_current_dbname
.size() == 0 ) {
320 // this is the first time through Retrieve, so ok
321 m_tar_record_state
= RS_NEXT
;
324 m_current_dbname
= dbname
;
325 return m_tar_record_state
;
329 bool Restore::BuildRecord(Barry::DBData
&data
,
331 const Barry::IConverter
*ic
)
333 // in this case, we are loading into m_record_data anyway,
334 // so no special handling is needed, like FetchRecord() needs.
335 switch( Retrieve(m_record_data
) )
339 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
340 data
.SetDBName(m_current_dbname
);
341 data
.SetIds(m_rec_type
, m_unique_id
);
342 data
.SetOffset(offset
);
344 int packet_size
= offset
+ m_record_data
.GetSize();
345 unsigned char *buf
= data
.UseData().GetBuffer(packet_size
);
346 memcpy(buf
+ offset
, m_record_data
.GetData(), m_record_data
.GetSize());
347 offset
+= m_record_data
.GetSize();
348 data
.UseData().ReleaseBuffer(packet_size
);
350 // clear loaded flag, as it has now been used
351 m_tar_record_state
= RS_EMPTY
;
358 throw std::logic_error(_("Invalid state in Restore::BuildRecord()"));
361 // process the end of database by returning false
362 // the next round will be valid, so set to RS_NEXT
363 m_tar_record_state
= RS_NEXT
;
367 // always return false at end of file
372 bool Restore::FetchRecord(Barry::DBData
&data
, const Barry::IConverter
*ic
)
374 // if the record has not yet been loaded, we can optimize
375 // the buffer, and pass in our own... otherwise, just copy
376 // the current buffer from m_record_data
378 // it is assumed here that Builder users will not alternate
379 // between calls to BuildRecord() and FetchRecord()
381 if( m_tar_record_state
== RS_EMPTY
) {
382 // BUT, if RS_DBEND is the next value, then we need
383 // to save the data for the next round... this
384 // optimization is almost more bother than it's worth :-)
385 if( Retrieve(data
.UseData()) == RS_DBEND
) {
386 m_record_data
= data
.GetData();
387 m_tar_record_state
= RS_NEXT
;
392 data
.UseData() = m_record_data
;
395 switch( m_tar_record_state
)
398 data
.SetVersion(Barry::DBData::REC_VERSION_1
);
399 data
.SetDBName(m_current_dbname
);
400 data
.SetIds(m_rec_type
, m_unique_id
);
403 // clear loaded flag, as it has now been used
404 m_tar_record_state
= RS_EMPTY
;
410 throw std::logic_error(_("Invalid state in Restore::FetchRecord()"));
413 // process the end of database by returning false
414 // the next round will be valid, so set to RS_NEXT
415 m_tar_record_state
= RS_NEXT
;
419 // always return false at end of file
424 bool Restore::EndOfFile() const
426 return m_tar_record_state
== RS_EOF
;