using namespace std;
-FileData::FileData( char _type,
- string _permissions,
- string _user,
- string _group,
- unsigned long long _size,
- unsigned long long _modified_date,
- unsigned long long _last_backup,
- string _name )
-: filetype( _type ),
- permissions( _permissions ),
- username( _user ),
- groupname( _group ),
- filesize( _size ),
- modified_date( _modified_date ),
- last_backup_date( _last_backup ),
- filename( _name )
-{}
-
vector<string> split( const string &line, char c, int limit = -1 ) {
string::size_type start = 0, end = 0;
return out;
}
-ostream &operator<<( const FileData *d, ostream &o ) {
- return operator<<( *d, o );
+ostream &operator<<( ostream &o, const FileData *d) {
+ return operator<<( o, *d );
}
-ostream &operator<<( const FileData &d, ostream &o ) {
+ostream &operator<<( ostream &o, const FileData &d ) {
o << d.getFileType() << ' ';
o << d.getPermissions() << ' ';
o << d.getUserName() << ' ';
istream &operator>>( istream &i, FileData &d ) {
string file_string;
- for( int c = i.get(); 0 != c && ! i.eof(); c = i.get() ) {
+ int c;
+ // Todo, don't use char_traits<char> directly here
+ for( c = i.get(); 0 != c && char_traits<char>::eof() != c; c = i.get() ) {
file_string.push_back( c );
}
+ if( char_traits<char>::eof() == c ) {
+ i.setstate( ios_base::eofbit );
+ }
if( 0 != file_string.size() ) {
// Example entry
d.setPermissions( vals[1] );
d.setUserName( vals[2] );
d.setGroupName( vals[3] );
- d.setFileSize( atoi( vals[4].c_str() ) );
- d.setModifiedDate( atoi( vals[5].c_str() ) );
- d.setLastBackupDate( atoi( vals[6].c_str() ) );
+ d.setFileSize( atoll( vals[4].c_str() ) );
+ d.setModifiedDate( atoll( vals[5].c_str() ) );
+ d.setLastBackupDate( atoll( vals[6].c_str() ) );
d.setFileName( vals[7] );
}
#include <iterator>
#include <algorithm>
#include <cassert>
+#include <ctime>
#include "filedata.hpp"
using namespace std;
-template<class ISTREAM, class SET>
-void populate_set( ISTREAM &in, SET &files ) {
+unsigned long long current_time() {
+ unsigned long long rc = 0;
+ time_t now_tt = time( 0 );
+ tm *now = localtime( &now_tt );
+ rc += ( now->tm_year + 1900ULL ) * 10000000000ULL;
+ rc += ( now->tm_mon + 1ULL ) * 100000000ULL;
+ rc += now->tm_mday * 1000000ULL;
+ rc += now->tm_hour * 10000ULL;
+ rc += now->tm_min * 100ULL;
+ rc += now->tm_sec;
+
+ return rc;
+}
+
+template<class I, class O>
+bool copy_until_full( I begin, I end, O out, unsigned long long &space ) {
+ const unsigned long long block_size = 0x200ULL;
+ bool complete = true;
+
+ I i = begin;
+ while( 0 != space && i != end ) {
+ unsigned long long size = (*i)->getFileSize();
+ unsigned long long blocks = size & ( ~(block_size-1) );
+ if( blocks < size ) blocks += block_size;
+
+ if( blocks <= space ) {
+ space -= blocks;
+ out = *i;
+ ++out;
+ } else {
+ // We missed a file that should be included so the backup is not complete
+ complete = false;
+ }
+ ++i;
+ }
+ return complete;
+}
+
+template<class SET>
+void populate_set( istream &in, SET &files ) {
do {
FileData *data = new FileData();
- in >> (*data);
- files.insert( data );
+ in >> data;
+ if( data->getFileName().size() ) {
+ files.insert( data );
+ } else {
+ delete data;
+ }
} while( ! in.eof() );
}
file_set backed_up;
ifstream db( "test.db" );
- populate_set( db, backed_up);
+ if( db && db.good() ) {
+ populate_set( db, backed_up );
+ }
// Now divide the two sets into three sets (added, deleted and common )
file_set added, deleted, common;
{ // This little block will copy the last_backup_date from the second set to the first
FileDataNameCmp cmp;
- file_set updated_mirror;
- set_union( current.begin(), current.end(),
- backed_up.begin(), backed_up.end(),
- inserter( updated_mirror, updated_mirror.begin() ),
+ file_set common_with_dates;
+ set_union( backed_up.begin(), backed_up.end(),
+ current.begin(), current.end(),
+ inserter( common_with_dates, common_with_dates.begin() ),
cmp );
- // TODO Now we need to copy the last_backup_date from
- file_set::iterator i = common.begin(), j = updated_mirror.begin();
+ file_set::iterator i = common.begin(), j = common_with_dates.begin();
for( ; i != common.end(); ++i, ++j ) {
(*i)->setLastBackupDate( (*j)->getLastBackupDate() );
}
}
// Now find the list of files to backup.
- file_set backup_set;
+ file_set backups;
// backup all added files
- copy( added.begin(), added.end(), inserter( backup_set, backup_set.begin() ) );
+ copy( added.begin(), added.end(), inserter( backups, backups.begin() ) );
// backup common files that have changed since the last backup date.
for( file_set::iterator i = common.begin(); i != common.end(); ++i ) {
if( (*i)->getLastBackupDate() < (*i)->getModifiedDate() ) {
- backup_set.insert( *i );
+ backups.insert( *i );
}
}
- // Now, sort the backup_set by filesize and build a list of up to SIZE
- file_vector backups_bysize;
- copy( backup_set.begin(), backup_set.end(), back_inserter( backups_bysize ) );
- FileDataSizeCmp sizecmp;
- sort( backups_bysize.begin(), backups_bysize.end(), sizecmp );
+ // Now, sort the backups by filesize and build a list of up to SIZE
+ file_vector backups_s;
+ copy( backups.begin(), backups.end(), back_inserter( backups_s ) );
- file_set final_set;
- unsigned long long bytes_available = 4700000000ULL; // 4.3 GBytes
+ FileDataSizeCmp sizecmp;
+ sort( backups_s.begin(), backups_s.end(), sizecmp );
- unsigned long long block_size = 512ULL;
+ file_set final;
+ unsigned long long space = 0x107c00000ULL; // 4220 MBytes
- bool complete = true;
+ insert_iterator<file_set> final_i( final, final.begin() );
// Copy files over until full or out of files
- file_vector::reverse_iterator i = backups_bysize.rbegin();
- while( 0 != bytes_available && i != backups_bysize.rend() ) {
- unsigned long long size = (*i)->getFileSize();
- unsigned long long blocks = size & ( ~(block_size-1) );
- if( blocks < size ) blocks += block_size;
- if( blocks <= bytes_available ) {
- bytes_available -= blocks;
- final_set.insert( *i );
- } else {
- // We missed a file that should be included so the backup is not complete
- complete = false;
- }
- ++i;
- }
+ bool complete = copy_until_full( backups_s.rbegin(),
+ backups_s.rend(),
+ final_i,
+ space );
// Now, sort the non-backed-up list by last_backup_date and back-fill
- if( 0 != bytes_available ) {
+ if( 0 != space ) {
file_vector leftovers;
FileDataNameCmp cmp;
- set_difference( current.begin(), current.end(),
- final_set.begin(), final_set.end(),
+ set_difference( current.begin(), current.end(),
+ final.begin(), final.end(),
back_inserter( leftovers ),
cmp );
FileDataLastBackupCmp lastbackupcmp;
sort( leftovers.begin(), leftovers.end(), lastbackupcmp );
- // Copy files over until full or out of files
- file_vector::const_iterator j = leftovers.begin();
- while( 0 != bytes_available && j != leftovers.end() ) {
- unsigned long long size = (*j)->getFileSize();
- unsigned long long blocks = size & ( ~(block_size-1) );
-
- if( blocks < size ) blocks += block_size;
-
- if( blocks <= bytes_available ) {
- bytes_available -= blocks;
- final_set.insert( *j );
- }
- ++j;
- }
+ copy_until_full( leftovers.begin(), leftovers.end(), final_i, space );
}
- // TODO Get 'now' from time clock
- unsigned long long now = 20051019211200ULL;
- for( file_set::iterator k = final_set.begin(); k != final_set.end(); ++k ) {
+ unsigned long long now = current_time();
+ for( file_set::iterator k = final.begin(); k != final.end(); ++k ) {
(*k)->setLastBackupDate( now );
}
ofstream dbout( "test.db" );
copy( current.begin(), current.end(), ostream_iterator<FileData*>( dbout, "" ) );
- // Write the 'final_set' list to stdout
- copy( final_set.begin(), final_set.end(), ostream_iterator<FileData*>( cout, "" ) );
+ // Write the 'final' list to stdout
+ copy( final.begin(), final.end(), ostream_iterator<FileData*>( cout, "" ) );
- // If ! complete then write a flag to /tmp
- if( ! complete ) {
- cerr << "incomplete" << endl;
- }
+ if( ! complete ) { cerr << "incomplete" << endl; }
// Clean-up
for( file_set::iterator i = backed_up.begin(); i != backed_up.end(); ++i ) { delete *i; }