diff --git a/libgrive/src/base/Resource.cc b/libgrive/src/base/Resource.cc index eb9c74c..d7d4ba8 100644 --- a/libgrive/src/base/Resource.cc +++ b/libgrive/src/base/Resource.cc @@ -77,13 +77,13 @@ void Resource::SetState( State new_state ) boost::bind( &Resource::SetState, _1, new_state ) ) ; } -void Resource::FromRemoteFolder( const Entry& remote, const DateTime& last_change ) +void Resource::FromRemoteFolder( const Entry& remote ) { fs::path path = Path() ; if ( !remote.IsEditable() ) Log( "folder %1% is read-only", path, log::verbose ) ; - + // already sync if ( fs::is_directory( path ) ) { @@ -91,48 +91,39 @@ void Resource::FromRemoteFolder( const Entry& remote, const DateTime& last_chang m_state = sync ; } - // remote file created after last sync, so remote is newer - else if ( remote.MTime() > last_change ) + else if ( fs::exists( path ) ) { - if ( fs::exists( path ) ) - { - // TODO: handle type change - Log( "%1% changed from folder to file", path, log::verbose ) ; - m_state = sync ; - } - else - { - // make all children as remote_new, if any - Log( "folder %1% is created in remote", path, log::verbose ) ; - SetState( remote_new ) ; - } + // TODO: handle type change + Log( "%1% changed from folder to file", path, log::verbose ) ; + m_state = sync ; } + + // remote file created after last sync, so remote is newer + // TODO: Check local index instead of last_sync time + else if ( remote.MTime() > last_sync ) + { + // make all children as remote_new, if any + Log( "folder %1% is created in remote", path, log::verbose ) ; + SetState( remote_new ) ; + } + else { - if ( fs::exists( path ) ) - { - // TODO: handle type chage - Log( "%1% changed from file to folder", path, log::verbose ) ; - m_state = sync ; - } - else - { - Log( "folder %1% is deleted in local", path, log::verbose ) ; - SetState( local_deleted ) ; - } + Log( "folder %1% is deleted in local", path, log::verbose ) ; + SetState( local_deleted ) ; } } /// Update the state according to information (i.e. Entry) from remote. This function /// compares the modification time and checksum of both copies and determine which /// one is newer. -void Resource::FromRemote( const Entry& remote, const DateTime& last_change ) +void Resource::FromRemote( const Entry& remote ) { // sync folder if ( remote.IsDir() && IsFolder() ) - FromRemoteFolder( remote, last_change ) ; + FromRemoteFolder( remote ) ; else - FromRemoteFile( remote, last_change ) ; + FromRemoteFile( remote ) ; AssignIDs( remote ) ; @@ -156,7 +147,7 @@ void Resource::AssignIDs( const Entry& remote ) } } -void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_change ) +void Resource::FromRemoteFile( const Entry& remote ) { assert( m_parent != 0 ) ; @@ -179,7 +170,8 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_change { Trace( "file %1% change stamp = %2%", Path(), remote.ChangeStamp() ) ; - if ( remote.MTime() > last_change || remote.ChangeStamp() > 0 ) + // TODO: Check local index instead of last_sync time + if ( remote.MTime() > last_sync || remote.ChangeStamp() > 0 ) { Log( "file %1% is created in remote (change %2%)", path, remote.ChangeStamp(), log::verbose ) ; @@ -214,7 +206,7 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_change assert( m_state != unknown ) ; // if remote is modified - if ( remote.MTime() > m_mtime ) + if ( remote.MTime().Sec() > m_mtime.Sec() ) { Log( "file %1% is changed in remote", path, log::verbose ) ; m_state = remote_changed ; @@ -233,7 +225,7 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_change /// Update the resource with the attributes of local file or directory. This /// function will propulate the fields in m_entry. -void Resource::FromLocal( const DateTime& last_sync, Val& state ) +void Resource::FromLocal( Val& state ) { assert( !m_json ); m_json = &state; @@ -246,37 +238,43 @@ void Resource::FromLocal( const DateTime& last_sync, Val& state ) os::Stat( path, &m_ctime, &m_size, &is_dir ) ; m_name = path.filename().string() ; - if ( !is_dir ) + m_kind = is_dir ? "folder" : "file"; + + bool is_changed; + if ( state.Has( "ctime" ) && (u64_t) m_ctime.Sec() <= state["ctime"].U64() && + ( is_dir || state.Has( "md5" ) ) ) { - m_kind = "file"; - if ( state.Has( "ctime" ) && state.Has( "md5" ) && (u64_t) m_ctime.Sec() == state["ctime"].U64() ) + if ( !is_dir ) m_md5 = state["md5"]; - else - { - m_md5 = crypt::MD5::Get( path ); - state.Set( "md5", Val( m_md5 ) ); - state.Set( "ctime", Val( m_ctime.Sec() ) ); - } - if ( state.Has( "srv_time" ) && m_mtime != DateTime() ) - m_mtime.Assign( state[ "srv_time" ].U64(), 0 ) ; + is_changed = false; } else { - m_kind = "folder"; - state.Del( "md5" ); - state.Del( "ctime" ); - state.Del( "srv_time" ); + if ( !is_dir ) + { + m_md5 = crypt::MD5::Get( path ); + // File is changed locally. TODO: Detect conflicts + is_changed = state.Has( "md5" ) && m_md5 != state["md5"].Str(); + state.Set( "md5", Val( m_md5 ) ); + } + else + is_changed = true; + state.Set( "ctime", Val( m_ctime.Sec() ) ); } + if ( state.Has( "srv_time" ) && m_mtime != DateTime() ) + m_mtime.Assign( state[ "srv_time" ].U64(), 0 ) ; + if ( is_dir ) + state.Del( "md5" ); // follow parent recursively if ( m_parent->m_state == local_new || m_parent->m_state == local_deleted ) - m_state = local_new ; - - // if the file is not created after last sync, assume file is - // remote_deleted first, it will be updated to sync/remote_changed - // in FromRemote() + m_state = m_parent->m_state ; else - m_state = ( m_ctime > last_sync ? local_new : remote_deleted ) ; + { + // Upload file if it is changed and remove if not. + // State will be updated to sync/remote_changed in FromRemote() + m_state = is_changed ? local_new : remote_deleted; + } } assert( m_state != unknown ) ; @@ -389,7 +387,7 @@ Resource* Resource::FindChild( const std::string& name ) } // try to change the state to "sync" -void Resource::Sync( Syncer *syncer, DateTime& sync_time, const Val& options ) +void Resource::Sync( Syncer *syncer, const Val& options ) { assert( m_state != unknown ) ; assert( !IsRoot() || m_state == sync ) ; // root folder is already synced @@ -410,15 +408,11 @@ void Resource::Sync( Syncer *syncer, DateTime& sync_time, const Val& options ) return; } - // we want the server sync time, so we will take the server time of the last file uploaded to store as the sync time - // m_mtime is updated to server modified time when the file is uploaded - sync_time = std::max(sync_time, m_mtime); - // if myself is deleted, no need to do the childrens if ( m_state != local_deleted && m_state != remote_deleted ) { std::for_each( m_child.begin(), m_child.end(), - boost::bind( &Resource::Sync, _1, syncer, boost::ref(sync_time), options ) ) ; + boost::bind( &Resource::Sync, _1, syncer, options ) ) ; if ( IsFolder() ) { // delete state of removed files @@ -512,7 +506,7 @@ void Resource::SyncSelf( Syncer* syncer, const Val& options ) break ; } - if ( m_state != local_deleted && m_state != remote_deleted && !IsFolder() ) + if ( m_state != local_deleted && m_state != remote_deleted ) { // Update server time of this file m_json->Set( "srv_time", Val( m_mtime.Sec() ) ); diff --git a/libgrive/src/base/Resource.hh b/libgrive/src/base/Resource.hh index 99b9315..a9b5ebf 100644 --- a/libgrive/src/base/Resource.hh +++ b/libgrive/src/base/Resource.hh @@ -106,10 +106,10 @@ public : bool HasID() const ; std::string MD5() const ; - void FromRemote( const Entry& remote, const DateTime& last_change ) ; - void FromLocal( const DateTime& last_sync, Val& state ) ; + void FromRemote( const Entry& remote ) ; + void FromLocal( Val& state ) ; - void Sync( Syncer* syncer, DateTime& sync_time, const Val& options ) ; + void Sync( Syncer* syncer, const Val& options ) ; void SetServerTime( const DateTime& time ) ; // children access @@ -129,8 +129,8 @@ private : private : void SetState( State new_state ) ; - void FromRemoteFolder( const Entry& remote, const DateTime& last_change ) ; - void FromRemoteFile( const Entry& remote, const DateTime& last_change ) ; + void FromRemoteFolder( const Entry& remote ) ; + void FromRemoteFile( const Entry& remote ) ; void DeleteLocal() ; void SetIndex() ; diff --git a/libgrive/src/base/ResourceTree.cc b/libgrive/src/base/ResourceTree.cc index 66bb379..363c5c6 100644 --- a/libgrive/src/base/ResourceTree.cc +++ b/libgrive/src/base/ResourceTree.cc @@ -123,11 +123,11 @@ void ResourceTree::Erase( Resource *coll ) s.erase( s.find( coll ) ) ; } -void ResourceTree::Update( Resource *coll, const Entry& e, const DateTime& last_change ) +void ResourceTree::Update( Resource *coll, const Entry& e ) { assert( coll != 0 ) ; - coll->FromRemote( e, last_change ) ; + coll->FromRemote( e ) ; ReInsert( coll ) ; } diff --git a/libgrive/src/base/ResourceTree.hh b/libgrive/src/base/ResourceTree.hh index c194518..f550004 100644 --- a/libgrive/src/base/ResourceTree.hh +++ b/libgrive/src/base/ResourceTree.hh @@ -75,7 +75,7 @@ public : void Insert( Resource *coll ) ; void Erase( Resource *coll ) ; - void Update( Resource *coll, const Entry& e, const DateTime& last_change ) ; + void Update( Resource *coll, const Entry& e ) ; Resource* Root() ; const Resource* Root() const ; diff --git a/libgrive/src/base/State.cc b/libgrive/src/base/State.cc index 0cfa5f8..d677d4b 100644 --- a/libgrive/src/base/State.cc +++ b/libgrive/src/base/State.cc @@ -65,14 +65,11 @@ State::State( const fs::path& filename, const Val& options ) : // the "-f" option will make grive always think remote is newer if ( force ) - { - m_last_change = DateTime() ; - m_last_sync = DateTime::Now() ; - } + m_last_sync = new DateTime() ; m_ign_re = boost::regex( m_ign.empty() ? "^\\.(grive|grive_state|trash)" : ( m_ign+"|^\\.(grive|grive_state|trash)" ) ); - Log( "last server change time: %1%; last sync time: %2%", m_last_change, m_last_sync, log::verbose ) ; + Log( "last sync time: %2%", m_last_sync, log::verbose ) ; } State::~State() @@ -85,7 +82,9 @@ void State::FromLocal( const fs::path& p ) { if ( !m_st.Has( "tree" ) ) m_st.Add( "tree", Val() ); - m_res.Root()->FromLocal( m_last_sync, m_st ) ; + // Remember m_update_sync just before reading local file tree + m_update_sync = DateTime::Now() ; + m_res.Root()->FromLocal( m_st ) ; FromLocal( p, m_res.Root(), m_st["tree"] ) ; } @@ -120,7 +119,7 @@ void State::FromLocal( const fs::path& p, Resource* folder, Val& tree ) if ( !tree.Has( fname ) ) tree.Add( fname, Val() ); Val& rec = tree[fname]; - c->FromLocal( m_last_sync, rec ) ; + c->FromLocal( rec ) ; if ( c->IsFolder() ) { if ( !rec.Has("tree") ) @@ -189,7 +188,7 @@ void State::FromChange( const Entry& e ) // entries in the change feed is always treated as newer in remote, // so we override the last sync time to 0 if ( Resource *res = m_res.FindByHref( e.SelfHref() ) ) - m_res.Update( res, e, DateTime() ) ; + m_res.Update( res, e ) ; } bool State::Update( const Entry& e ) @@ -205,7 +204,7 @@ bool State::Update( const Entry& e ) Log( "%1% is ignored by grive", path, log::verbose ) ; return true; } - m_res.Update( res, e, m_last_change ) ; + m_res.Update( res, e ) ; return true; } else if ( Resource *parent = m_res.FindByHref( e.ParentHref() ) ) @@ -225,7 +224,7 @@ bool State::Update( const Entry& e ) if ( child ) { // since we are updating the ID and Href, we need to remove it and re-add it. - m_res.Update( child, e, m_last_change ) ; + m_res.Update( child, e ) ; } // folder entry exist in google drive, but not local. we should create @@ -238,7 +237,7 @@ bool State::Update( const Entry& e ) m_res.Insert( child ) ; // update the state of the resource - m_res.Update( child, e, m_last_change ) ; + m_res.Update( child, e ) ; } return true ; @@ -265,16 +264,13 @@ State::iterator State::end() void State::Read( const fs::path& filename ) { m_last_sync.Assign( 0 ) ; - m_last_change.Assign( 0 ) ; try { File file( filename ) ; m_st = ParseJson( file ); Val last_sync = m_st["last_sync"] ; - Val last_change = m_st.Has( "last_change" ) ? m_st["last_change"] : m_st["last_sync"] ; m_last_sync.Assign( last_sync["sec"].Int(), last_sync["nsec"].Int() ) ; - m_last_change.Assign( last_change["sec"].Int(), last_change["nsec"].Int() ) ; m_ign = m_st.Has( "ignore_regexp" ) ? m_st["ignore_regexp"].Str() : std::string(); m_cstamp = m_st["change_stamp"].Int() ; @@ -290,12 +286,7 @@ void State::Write( const fs::path& filename ) last_sync.Add( "sec", Val( (int)m_last_sync.Sec() ) ); last_sync.Add( "nsec", Val( (unsigned)m_last_sync.NanoSec() ) ); - Val last_change ; - last_change.Add( "sec", Val( (int)m_last_change.Sec() ) ); - last_change.Add( "nsec", Val( (unsigned)m_last_change.NanoSec() ) ); - m_st.Set( "last_sync", last_sync ) ; - m_st.Set( "last_change", last_change ) ; m_st.Set( "change_stamp", Val( m_cstamp ) ) ; m_st.Set( "ignore_regexp", Val( m_ign ) ) ; @@ -305,24 +296,9 @@ void State::Write( const fs::path& filename ) void State::Sync( Syncer *syncer, const Val& options ) { - // set the last sync time from the time returned by the server for the last file synced - // if the sync time hasn't changed (i.e. now files have been uploaded) // set the last sync time to the time on the client - // ideally because we compare server file times to the last sync time - // the last sync time would always be a server time rather than a client time - // TODO - WARNING - do we use the last sync time to compare to client file times - // need to check if this introduces a new problem - DateTime last_change_time = m_last_change; - m_res.Root()->Sync( syncer, last_change_time, options ) ; - - if ( last_change_time == m_last_change ) - Trace( "nothing changed at the server side since %1%", m_last_change ) ; - else - { - Trace( "updating last server-side change time to %1%", last_change_time ) ; - m_last_change = last_change_time; - } - m_last_sync = DateTime::Now(); + m_res.Root()->Sync( syncer, options ) ; + m_last_sync = m_update_sync; } long State::ChangeStamp() const diff --git a/libgrive/src/base/State.hh b/libgrive/src/base/State.hh index 353031d..5cb8564 100644 --- a/libgrive/src/base/State.hh +++ b/libgrive/src/base/State.hh @@ -75,7 +75,7 @@ private : private : ResourceTree m_res ; DateTime m_last_sync ; - DateTime m_last_change ; + DateTime m_update_sync ; int m_cstamp ; std::string m_ign ; boost::regex m_ign_re ; diff --git a/libgrive/test/base/ResourceTest.cc b/libgrive/test/base/ResourceTest.cc index 3aebffd..f5cdc12 100644 --- a/libgrive/test/base/ResourceTest.cc +++ b/libgrive/test/base/ResourceTest.cc @@ -63,7 +63,7 @@ void ResourceTest::TestNormal( ) entry.AddElement( "updated" ).AddText( "2012-05-09T16:13:22.401Z" ) ; Entry1 remote( entry ) ; - subject.FromRemote( remote, DateTime() ) ; + subject.FromRemote( remote ) ; GRUT_ASSERT_EQUAL( "local_changed", subject.StateStr() ) ; }