# 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-2011, 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                         bool default_all_db)
    00040         {
    00041                 int count = 0;
    00042                 std::string name, last_name;
    00043                 bool good = false;
    00044
    00045                 while( tar.ReadNextFilenameOnly(name) ) {
    00046                         std::string::size_type pos = name.rfind('/');
    00047                         if( pos == std::string::npos )
    00048                                 continue;       // bad name
    00049                         std::string dbname = name.substr(0, pos);
    00050
    00051                         if( dbname != last_name ) {
    00052                                 last_name = dbname;
    00053                                 good = (default_all_db && restoreList.size() == 0) ||
    00054                                         restoreList.IsSelected(dbname);
    00055                         }
    00056                         if( good )
    00057                                 count++;
    00058                 }
    00059                 return count;
    00060         }
    00061
    00062 }
    00063
    00064 //////////////////////////////////////////////////////////////////////////////
    00065 // Static Restore members
    00066
    00067 /// Splits a tarpath of the form "DBName/DBID" into separate string values.
    00068 /// Returns true if successful, false if tarpath is a bad name.
    00069 bool Restore::SplitTarPath(const std::string &tarpath,
    00070                                    std::string &dbname,
    00071                                    std::string &dbid_text,
    00072                                    uint8_t &dbrectype,
    00073                                    uint32_t &dbid)
    00074 {
    00075         std::string::size_type pos = tarpath.rfind('/');
    00076         if( pos == std::string::npos )
    00077                 return false;           // bad name
    00078
    00079         dbname = tarpath.substr(0, pos);
    00080         dbid_text = tarpath.substr(pos + 1);
    00081         if( dbname.size() == 0 || dbid_text.size() == 0 )
    00082                 return false;           // bad name
    00083
    00084         std::istringstream iss(dbid_text);
    00085         unsigned int temp;
    00086         iss >> std::hex >> dbid >> temp;
    00087         dbrectype = (uint8_t) temp;
    00088
    00089         return true;
    00090 }
    00091
    00092
    00093 //////////////////////////////////////////////////////////////////////////////
    00094 // Restore - constructors
    00095
    00096 Restore::Restore(const std::string &tarpath, bool default_all_db)
    00097         : m_tarpath(tarpath)
    00098         , m_default_all_db(default_all_db)
    00099         , m_tar_record_state(RS_EMPTY)
    00100         , m_rec_type(0)
    00101         , m_unique_id(0)
    00102 {
    00103         try {
    00104                 m_tar.reset( new reuse::TarFile(tarpath.c_str(), false,
    00105                                         &reuse::gztar_ops_nonthread, true) );
    00106         }
    00107         catch( reuse::TarFile::TarError &te ) {
    00108                 throw Barry::RestoreError(te.what());
    00109         }
    00110 }
    00111
    00112 Restore::~Restore()
    00113 {
    00114 }
    00115
    00116
    00117 //////////////////////////////////////////////////////////////////////////////
    00118 // Restore - Protected helpers
    00119
    00120 bool Restore::IsSelected(const std::string &dbName) const
    00121 {
    00122         // if nothing is in the list, use default
    00123         if( m_dbList.size() == 0 )
    00124                 return m_default_all_db;
    00125         else
    00126                 return m_dbList.IsSelected(dbName);
    00127 }
    00128
    00129
    00130 //////////////////////////////////////////////////////////////////////////////
    00131 // Restore - Public API
    00132
    00133 void Restore::AddDB(const std::string &dbName)
    00134 {
    00135         if( find(m_dbList.begin(), m_dbList.end(), dbName) == m_dbList.end() ) {
    00136                 // only add it if it is not already in the list
    00137                 m_dbList.push_back(dbName);
    00138         }
    00139 }
    00140
    00141 void Restore::Add(const DBListType &dbList)
    00142 {
    00143         for( DBListType::const_iterator i = dbList.begin();
    00144                 i != dbList.end();
    00145                 ++i )
    00146         {
    00147                 AddDB(*i);
    00148         }
    00149 }
    00150
    00151 void Restore::SkipCurrentDB()
    00152 {
    00153         // skip all records until next DB
    00154         try {
    00155                 while( Retrieve(m_record_data) == RS_NEXT ) {
    00156                         std::cerr << "Skipping: "
    00157                                 << m_current_dbname << "/"
    00158                                 << m_tar_id_text << std::endl;
    00159                         m_tar_record_state = RS_EMPTY;
    00160                 }
    00161         }
    00162         catch( reuse::TarFile::TarError & ) {
    00163                 m_tar_record_state = RS_EOF;
    00164         }
    00165 }
    00166
    00167 unsigned int Restore::GetRecordTotal() const
    00168 {
    00169         return GetRecordTotal(m_tarpath, m_dbList, m_default_all_db);
    00170 }
    00171
    00172 unsigned int Restore::GetRecordTotal(const std::string &tarpath,
    00173                                         const DBListType &dbList,
    00174                                         bool default_all_db)
    00175 {
    00176         unsigned int count = 0;
    00177
    00178         std::auto_ptr<reuse::TarFile> tar;
    00179
    00180         try {
    00181                 // do a scan through the tar file
    00182                 tar.reset( new reuse::TarFile(tarpath.c_str(), false,
    00183                                 &reuse::gztar_ops_nonthread, true) );
    00184                 count = CountFiles(*tar, dbList, default_all_db);
    00185         }
    00186         catch( reuse::TarFile::TarError &te ) {
    00187                 throw Barry::RestoreError(te.what());
    00188         }
    00189         return count;
    00190 }
    00191
    00192 bool Restore::GetNextMeta(DBData &data)
    00193 {
    00194         // always use m_record_data here, so that we don't lose access
    00195         // to the actual record data for future calls to BuildRecord()
    00196         // and FetchRecord()
    00197         if( m_tar_record_state == RS_EMPTY ) {
    00198                 Retrieve(m_record_data);
    00199         }
    00200
    00201         // fill in the meta data that will be returned in the next call
    00202         // to BuildRecord() or FetchRecord()... this is only valid if
    00203         // the state is RS_NEXT
    00204         switch( m_tar_record_state )
    00205         {
    00206         case RS_NEXT:
    00207                 data.SetVersion(Barry::DBData::REC_VERSION_1);
    00208                 data.SetDBName(m_current_dbname);
    00209                 data.SetIds(m_rec_type, m_unique_id);
    00210                 data.SetOffset(0);
    00211                 return true;
    00212
    00213         default:
    00214                 return false;
    00215         }
    00216 }
    00217
    00218
    00219 //////////////////////////////////////////////////////////////////////////////
    00220 // Barry::Builder overrides
    00221
    00222 Restore::RetrievalState Restore::Retrieve(Data &record_data)
    00223 {
    00224         // don't do anything unless we're empty
    00225         if( m_tar_record_state != RS_EMPTY )
    00226                 return m_tar_record_state;
    00227
    00228         // search for a valid record
    00229         for(;;) {
    00230                 // load record data from tar file
    00231                 std::string filename;
    00232                 if( !m_tar->ReadNextFile(filename, record_data) ) {
    00233                         // assume end of file
    00234                         return m_tar_record_state = RS_EOF;
    00235                 }
    00236                 m_tar_record_state = RS_UNKNOWN;
    00237
    00238                 // split record filename into dbname and ID
    00239                 std::string dbname;
    00240                 if( !SplitTarPath(filename, dbname, m_tar_id_text, m_rec_type, m_unique_id) ) {
    00241                         // invalid filename, skip it
    00242                         std::cerr << "Skipping invalid tar record: " << filename << std::endl;
    00243                         continue;
    00244                 }
    00245
    00246                 // are we working on the same dbname as last time?
    00247                 // if so, go ahead!
    00248                 if( m_current_dbname == dbname ) {
    00249                         return m_tar_record_state = RS_NEXT;
    00250                 }
    00251
    00252                 // DIFFERENT DBNAME from here on down!
    00253                 m_tar_record_state = RS_DBEND;
    00254
    00255                 // does the filter allow this record?
    00256                 // if not, skip it and continue looking
    00257                 if( !IsSelected(dbname) ) {
    00258                         continue;
    00259                 }
    00260
    00261                 // all checks pass, load the new dbname, and return DBEND
    00262                 // if we are on a dbname boundary
    00263                 if( m_current_dbname.size() == 0 ) {
    00264                         // this is the first time through Retrieve, so ok
    00265                         m_tar_record_state = RS_NEXT;
    00266                 }
    00267
    00268                 m_current_dbname = dbname;
    00269                 return m_tar_record_state;
    00270         }
    00271 }
    00272
    00273 bool Restore::BuildRecord(Barry::DBData &data,
    00274                           size_t &offset,
    00275                           const Barry::IConverter *ic)
    00276 {
    00277         // in this case, we are loading into m_record_data anyway,
    00278         // so no special handling is needed, like FetchRecord() needs.
    00279         switch( Retrieve(m_record_data) )
    00280         {
    00281         case RS_NEXT:
    00282                 {
    00283                         data.SetVersion(Barry::DBData::REC_VERSION_1);
    00284                         data.SetDBName(m_current_dbname);
    00285                         data.SetIds(m_rec_type, m_unique_id);
    00286                         data.SetOffset(offset);
    00287
    00288                         int packet_size = offset + m_record_data.GetSize();
    00289                         unsigned char *buf = data.UseData().GetBuffer(packet_size);
    00290                         memcpy(buf + offset, m_record_data.GetData(), m_record_data.GetSize());
    00291                         offset += m_record_data.GetSize();
    00292                         data.UseData().ReleaseBuffer(packet_size);
    00293
    00294                         // clear loaded flag, as it has now been used
    00295                         m_tar_record_state = RS_EMPTY;
    00296                         return true;
    00297                 }
    00298
    00299         case RS_EMPTY:
    00300         case RS_UNKNOWN:
    00301         default:
    00302                 throw std::logic_error("Invalid state in Restore::BuildRecord()");
    00303
    00304         case RS_DBEND:
    00305                 // process the end of database by returning false
    00306                 // the next round will be valid, so set to RS_NEXT
    00307                 m_tar_record_state = RS_NEXT;
    00308                 return false;
    00309
    00310         case RS_EOF:
    00311                 // always return false at end of file
    00312                 return false;
    00313         }
    00314 }
    00315
    00316 bool Restore::FetchRecord(Barry::DBData &data, const Barry::IConverter *ic)
    00317 {
    00318         // if the record has not yet been loaded, we can optimize
    00319         // the buffer, and pass in our own... otherwise, just copy
    00320         // the current buffer from m_record_data
    00321         //
    00322         // it is assumed here that Builder users will not alternate
    00323         // between calls to BuildRecord() and FetchRecord()
    00324         //
    00325         if( m_tar_record_state == RS_EMPTY ) {
    00326                 // BUT, if RS_DBEND is the next value, then we need
    00327                 // to save the data for the next round... this
    00328                 // optimization is almost more bother than it's worth :-)
    00329                 if( Retrieve(data.UseData()) == RS_DBEND ) {
    00330                         m_record_data = data.GetData();
    00331                         m_tar_record_state = RS_NEXT;
    00332                         return false;
    00333                 }
    00334         }
    00335         else {
    00336                 data.UseData() = m_record_data;
    00337         }
    00338
    00339         switch( m_tar_record_state )
    00340         {
    00341         case RS_NEXT:
    00342                 data.SetVersion(Barry::DBData::REC_VERSION_1);
    00343                 data.SetDBName(m_current_dbname);
    00344                 data.SetIds(m_rec_type, m_unique_id);
    00345                 data.SetOffset(0);
    00346
    00347                 // clear loaded flag, as it has now been used
    00348                 m_tar_record_state = RS_EMPTY;
    00349                 return true;
    00350
    00351         case RS_EMPTY:
    00352         case RS_UNKNOWN:
    00353         default:
    00354                 throw std::logic_error("Invalid state in Restore::FetchRecord()");
    00355
    00356         case RS_DBEND:
    00357                 // process the end of database by returning false
    00358                 // the next round will be valid, so set to RS_NEXT
    00359                 m_tar_record_state = RS_NEXT;
    00360                 return false;
    00361
    00362         case RS_EOF:
    00363                 // always return false at end of file
    00364                 return false;
    00365         }
    00366 }
    00367
    00368 bool Restore::EndOfFile() const
    00369 {
    00370         return m_tar_record_state == RS_EOF;
    00371 }
    00372
    00373 } // namespace Barry
    00374