restore.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       restore.cc
00003 ///             Builder class for restoring from Barry Backup files
00004 ///
00005 
00006 /*
00007     Copyright (C) 2010-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "restore.h"
00023 #include "tarfile.h"
00024 #include "error.h"
00025 #include <sstream>
00026 #include <iomanip>
00027 #include <iostream>
00028 #include <string.h>
00029 #include <algorithm>
00030 
00031 using namespace std;
00032 
00033 namespace Barry {
00034 
00035 namespace {
00036 
00037         int CountFiles(reuse::TarFile &tar,
00038                         const Barry::Restore::DBListType &restoreList,
00039                         Barry::Restore::DBListType *available,
00040                         bool default_all_db)
00041         {
00042                 int count = 0;
00043                 std::string name, last_name;
00044                 bool good = false;
00045 
00046                 while( tar.ReadNextFilenameOnly(name) ) {
00047                         std::string::size_type pos = name.rfind('/');
00048                         if( pos == std::string::npos )
00049                                 continue;       // bad name
00050                         std::string dbname = name.substr(0, pos);
00051 
00052                         if( dbname != last_name ) {
00053                                 last_name = dbname;
00054                                 good = (default_all_db && restoreList.size() == 0) ||
00055                                         restoreList.IsSelected(dbname);
00056 
00057                                 if( good && available )
00058                                         available->push_back(dbname);
00059                         }
00060                         if( good )
00061                                 count++;
00062                 }
00063                 return count;
00064         }
00065 
00066 }
00067 
00068 //////////////////////////////////////////////////////////////////////////////
00069 // Static Restore members
00070 
00071 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
00072 /// Returns true if successful, false if tarpath is a bad name.
00073 bool Restore::SplitTarPath(const std::string &tarpath,
00074                                    std::string &dbname,
00075                                    std::string &dbid_text,
00076                                    uint8_t &dbrectype,
00077                                    uint32_t &dbid)
00078 {
00079         std::string::size_type pos = tarpath.rfind('/');
00080         if( pos == std::string::npos )
00081                 return false;           // bad name
00082 
00083         dbname = tarpath.substr(0, pos);
00084         dbid_text = tarpath.substr(pos + 1);
00085         if( dbname.size() == 0 || dbid_text.size() == 0 )
00086                 return false;           // bad name
00087 
00088         std::istringstream iss(dbid_text);
00089         unsigned int temp;
00090         iss >> std::hex >> dbid >> temp;
00091         dbrectype = (uint8_t) temp;
00092 
00093         return true;
00094 }
00095 
00096 
00097 //////////////////////////////////////////////////////////////////////////////
00098 // Restore - constructors
00099 
00100 Restore::Restore(const std::string &tarpath, bool default_all_db)
00101         : m_tarpath(tarpath)
00102         , m_default_all_db(default_all_db)
00103         , m_tar_record_state(RS_EMPTY)
00104         , m_rec_type(0)
00105         , m_unique_id(0)
00106 {
00107         try {
00108                 m_tar.reset( new reuse::TarFile(tarpath.c_str(), false,
00109                                         &reuse::gztar_ops_nonthread, true) );
00110         }
00111         catch( reuse::TarFile::TarError &te ) {
00112                 throw Barry::RestoreError(te.what());
00113         }
00114 }
00115 
00116 Restore::~Restore()
00117 {
00118 }
00119 
00120 
00121 //////////////////////////////////////////////////////////////////////////////
00122 // Restore - Protected helpers
00123 
00124 bool Restore::IsSelected(const std::string &dbName) const
00125 {
00126         // if in skip list, always return false,
00127         // if nothing is in the main list, use default
00128         // otherwise, only return true if specifically selected
00129         if( m_dbSkipList.IsSelected(dbName) )
00130                 return false;
00131         else if( m_dbList.size() == 0 )
00132                 return m_default_all_db;
00133         else
00134                 return m_dbList.IsSelected(dbName);
00135 }
00136 
00137 
00138 //////////////////////////////////////////////////////////////////////////////
00139 // Restore - Public API
00140 
00141 void Restore::AddDB(const std::string &dbName)
00142 {
00143         if( find(m_dbList.begin(), m_dbList.end(), dbName) == m_dbList.end() ) {
00144                 // only add it if it is not already in the list
00145                 m_dbList.push_back(dbName);
00146         }
00147 }
00148 
00149 void Restore::Add(const DBListType &dbList)
00150 {
00151         for( DBListType::const_iterator i = dbList.begin();
00152                 i != dbList.end();
00153                 ++i )
00154         {
00155                 AddDB(*i);
00156         }
00157 }
00158 
00159 void Restore::Add(const DatabaseDatabase &dbdb)
00160 {
00161         for( DatabaseDatabase::DatabaseArrayType::const_iterator i = dbdb.Databases.begin();
00162                 i != dbdb.Databases.end();
00163                 ++i )
00164         {
00165                 AddDB(i->Name);
00166         }
00167 }
00168 
00169 void Restore::AddSkipDB(const std::string &dbName)
00170 {
00171         if( find(m_dbSkipList.begin(), m_dbSkipList.end(), dbName) == m_dbSkipList.end() ) {
00172                 // only add it if it is not already in the list
00173                 m_dbSkipList.push_back(dbName);
00174         }
00175 }
00176 
00177 void Restore::SkipCurrentDB()
00178 {
00179         // skip all records until next DB
00180         try {
00181                 Restore::RetrievalState state;
00182                 while( (state = Retrieve(m_record_data)) == RS_NEXT ) {
00183                         std::cerr << "Skipping: "
00184                                 << m_current_dbname << "/"
00185                                 << m_tar_id_text << std::endl;
00186                         m_tar_record_state = RS_EMPTY;
00187                 }
00188 
00189                 if( state == RS_DBEND ) {
00190                         // process the end of database, so that user is free
00191                         // to call GetNextMeta() or Retrieve() or BuildRecord()
00192                         m_tar_record_state = RS_NEXT;
00193                 }
00194         }
00195         catch( reuse::TarFile::TarError & ) {
00196                 m_tar_record_state = RS_EOF;
00197         }
00198 }
00199 
00200 unsigned int Restore::GetRecordTotal() const
00201 {
00202         return GetRecordTotal(m_tarpath, m_dbList, m_default_all_db);
00203 }
00204 
00205 unsigned int Restore::GetRecordTotal(const std::string &tarpath,
00206                                         const DBListType &dbList,
00207                                         bool default_all_db)
00208 {
00209         unsigned int count = 0;
00210 
00211         std::auto_ptr<reuse::TarFile> tar;
00212 
00213         try {
00214                 // do a scan through the tar file
00215                 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
00216                                 &reuse::gztar_ops_nonthread, true) );
00217                 count = CountFiles(*tar, dbList, 0, default_all_db);
00218         }
00219         catch( reuse::TarFile::TarError &te ) {
00220                 throw Barry::RestoreError(te.what());
00221         }
00222         return count;
00223 }
00224 
00225 Barry::Restore::DBListType Restore::GetDBList() const
00226 {
00227         return GetDBList(m_tarpath);
00228 }
00229 
00230 Barry::Restore::DBListType Restore::GetDBList(const std::string &tarpath)
00231 {
00232         std::auto_ptr<reuse::TarFile> tar;
00233         DBListType available, empty;
00234 
00235         try {
00236                 // do a scan through the tar file
00237                 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
00238                                 &reuse::gztar_ops_nonthread, true) );
00239                 CountFiles(*tar, empty, &available, true);
00240                 return available;
00241         }
00242         catch( reuse::TarFile::TarError &te ) {
00243                 throw Barry::RestoreError(te.what());
00244         }
00245 }
00246 
00247 bool Restore::GetNextMeta(DBData &data)
00248 {
00249         // always use m_record_data here, so that we don't lose access
00250         // to the actual record data for future calls to BuildRecord()
00251         // and FetchRecord()
00252         if( m_tar_record_state == RS_EMPTY ) {
00253                 Retrieve(m_record_data);
00254         }
00255 
00256         // fill in the meta data that will be returned in the next call
00257         // to BuildRecord() or FetchRecord()... this is only valid if
00258         // the state is RS_NEXT
00259         switch( m_tar_record_state )
00260         {
00261         case RS_NEXT:
00262                 data.SetVersion(Barry::DBData::REC_VERSION_1);
00263                 data.SetDBName(m_current_dbname);
00264                 data.SetIds(m_rec_type, m_unique_id);
00265                 data.SetOffset(0);
00266                 return true;
00267 
00268         default:
00269                 return false;
00270         }
00271 }
00272 
00273 
00274 //////////////////////////////////////////////////////////////////////////////
00275 // Barry::Builder overrides
00276 
00277 Restore::RetrievalState Restore::Retrieve(Data &record_data)
00278 {
00279         // don't do anything unless we're empty
00280         if( m_tar_record_state != RS_EMPTY )
00281                 return m_tar_record_state;
00282 
00283         // search for a valid record
00284         for(;;) {
00285                 // load record data from tar file
00286                 std::string filename;
00287                 if( !m_tar->ReadNextFile(filename, record_data) ) {
00288                         // assume end of file
00289                         return m_tar_record_state = RS_EOF;
00290                 }
00291                 m_tar_record_state = RS_UNKNOWN;
00292 
00293                 // split record filename into dbname and ID
00294                 std::string dbname;
00295                 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
00296                         // invalid filename, skip it
00297                         std::cerr << "Skipping invalid tar record: " << filename << std::endl;
00298                         continue;
00299                 }
00300 
00301                 // are we working on the same dbname as last time?
00302                 // if so, go ahead!
00303                 if( m_current_dbname == dbname ) {
00304                         return m_tar_record_state = RS_NEXT;
00305                 }
00306 
00307                 // DIFFERENT DBNAME from here on down!
00308                 m_tar_record_state = RS_DBEND;
00309 
00310                 // does the filter allow this record?
00311                 // if not, skip it and continue looking
00312                 if( !IsSelected(dbname) ) {
00313                         continue;
00314                 }
00315 
00316                 // all checks pass, load the new dbname, and return DBEND
00317                 // if we are on a dbname boundary
00318                 if( m_current_dbname.size() == 0 ) {
00319                         // this is the first time through Retrieve, so ok
00320                         m_tar_record_state = RS_NEXT;
00321                 }
00322 
00323                 m_current_dbname = dbname;
00324                 return m_tar_record_state;
00325         }
00326 }
00327 
00328 bool Restore::BuildRecord(Barry::DBData &data,
00329                           size_t &offset,
00330                           const Barry::IConverter *ic)
00331 {
00332         // in this case, we are loading into m_record_data anyway,
00333         // so no special handling is needed, like FetchRecord() needs.
00334         switch( Retrieve(m_record_data) )
00335         {
00336         case RS_NEXT:
00337                 {
00338                         data.SetVersion(Barry::DBData::REC_VERSION_1);
00339                         data.SetDBName(m_current_dbname);
00340                         data.SetIds(m_rec_type, m_unique_id);
00341                         data.SetOffset(offset);
00342 
00343                         int packet_size = offset + m_record_data.GetSize();
00344                         unsigned char *buf = data.UseData().GetBuffer(packet_size);
00345                         memcpy(buf + offset, m_record_data.GetData(), m_record_data.GetSize());
00346                         offset += m_record_data.GetSize();
00347                         data.UseData().ReleaseBuffer(packet_size);
00348 
00349                         // clear loaded flag, as it has now been used
00350                         m_tar_record_state = RS_EMPTY;
00351                         return true;
00352                 }
00353 
00354         case RS_EMPTY:
00355         case RS_UNKNOWN:
00356         default:
00357                 throw std::logic_error("Invalid state in Restore::BuildRecord()");
00358 
00359         case RS_DBEND:
00360                 // process the end of database by returning false
00361                 // the next round will be valid, so set to RS_NEXT
00362                 m_tar_record_state = RS_NEXT;
00363                 return false;
00364 
00365         case RS_EOF:
00366                 // always return false at end of file
00367                 return false;
00368         }
00369 }
00370 
00371 bool Restore::FetchRecord(Barry::DBData &data, const Barry::IConverter *ic)
00372 {
00373         // if the record has not yet been loaded, we can optimize
00374         // the buffer, and pass in our own... otherwise, just copy
00375         // the current buffer from m_record_data
00376         //
00377         // it is assumed here that Builder users will not alternate
00378         // between calls to BuildRecord() and FetchRecord()
00379         //
00380         if( m_tar_record_state == RS_EMPTY ) {
00381                 // BUT, if RS_DBEND is the next value, then we need
00382                 // to save the data for the next round... this
00383                 // optimization is almost more bother than it's worth :-)
00384                 if( Retrieve(data.UseData()) == RS_DBEND ) {
00385                         m_record_data = data.GetData();
00386                         m_tar_record_state = RS_NEXT;
00387                         return false;
00388                 }
00389         }
00390         else {
00391                 data.UseData() = m_record_data;
00392         }
00393 
00394         switch( m_tar_record_state )
00395         {
00396         case RS_NEXT:
00397                 data.SetVersion(Barry::DBData::REC_VERSION_1);
00398                 data.SetDBName(m_current_dbname);
00399                 data.SetIds(m_rec_type, m_unique_id);
00400                 data.SetOffset(0);
00401 
00402                 // clear loaded flag, as it has now been used
00403                 m_tar_record_state = RS_EMPTY;
00404                 return true;
00405 
00406         case RS_EMPTY:
00407         case RS_UNKNOWN:
00408         default:
00409                 throw std::logic_error("Invalid state in Restore::FetchRecord()");
00410 
00411         case RS_DBEND:
00412                 // process the end of database by returning false
00413                 // the next round will be valid, so set to RS_NEXT
00414                 m_tar_record_state = RS_NEXT;
00415                 return false;
00416 
00417         case RS_EOF:
00418                 // always return false at end of file
00419                 return false;
00420         }
00421 }
00422 
00423 bool Restore::EndOfFile() const
00424 {
00425         return m_tar_record_state == RS_EOF;
00426 }
00427 
00428 } // namespace Barry
00429