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 

Generated on Tue Mar 1 17:50:16 2011 for Barry by  doxygen 1.5.6