#include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include <stdhdrs.h> #include <strbuf.h> #include <error.h> #include "fileio.h" #include "journal.h" #include "msgjrep.h" #include "err.h" void Journal::SetName( StrPtr *Jval ) { if( Jval ) name.Set( Jval ); else name.Set( "journal" ); } char *Journal::GetName() { return name.Text(); } off_tL Journal::GetSize() { return statbuf.st_size; } int Journal::GetSequence() { return sequence; } bool Journal::Init( StrPtr *Jval, Error *e ) { SetName( Jval ); return true; } bool Journal::Open( Error *e ) { if( ( journalfd = openL( GetName(), O_RDONLY ) ) == -1 ) return syserr( "open", GetName(), e ); return true; } bool Journal::Seek( off_tL offset, Error *e ) { if( lseekL( journalfd, offset, SEEK_SET ) == -1 ) return syserr( "seek", GetName(), e ); return true; } bool Journal::Read( char *buf, size_t count, ssize_t *n, Error *e ) { if( ( *n = read( journalfd, buf, count ) ) == -1 ) return syserr( "read", GetName(), e ); return true; } bool Journal::ReadSequence( Error *e ) { int attempts; #define BUFSIZE 63 char buf[ BUFSIZE + 1 ]; char *bufptr; ssize_t n; int version; if( !Seek( 0, e ) ) return false; /* * Read the @vv@ record at the beginning of the journal. Since the * server truncates the journal without locking it first during * journal rotation, several attempts at reading the @vv@ record * are needed. The retry logic is needed since it is possible that * a single read here could occur between the inode updated as a * result of truncating the journal and the inode updated as a * result of writing of the @vv@ record. */ for( attempts = 0; attempts < 10; attempts++ ) { if( !Read( buf, BUFSIZE, &n, e ) ) return false; if( n ) break; sleep( 1 ); } if( n < 32 ) { e->Set( MsgJrep::VVTooShort ); return false; } if( strncmp( buf, "@vv@ ", 5 ) ) { e->Set( MsgJrep::VVNotFirst ); return false; } buf[ BUFSIZE ] = '\0'; version = strtol( &buf[ 5 ], &bufptr, 10 ); if( version != 0 ) { e->Set( MsgJrep::VVUnknownVersion ) << version; return false; } if( strncmp( bufptr, " @db.counters@ @journal@ ", 25 ) ) { e->Set( MsgJrep::VVFirstNotJournal ); return false; } bufptr += 25; if( !sscanf( bufptr, "%d", &sequence ) ) { e->Set( MsgJrep::VVSscanfFail ); return false; } return true; } bool Journal::Stat( Error *e ) { if( fstatL( journalfd, &statbuf ) == -1 ) return syserr( "stat", GetName(), e ); return true; } bool Journal::Close( Error *e ) { if( close( journalfd ) == -1 ) return syserr( "close", GetName(), e ); return true; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 7886 | Michael Shields |
Correctly handle 2010.2 journals, which have an @nx@ record as the first record in the journal after the journal has been rotated. |
||
#3 | 6439 | Michael Shields |
Updating for the 2008.1 p4d. The format of the @vv@ record changed in the 2008.1 p4d. The @vv@ record is the one and only journal record the format of which is important to p4jrep. This p4jrep can also be used with prior releases of p4d. Version string: p4jrep Version 0.91 (beta) |
||
#2 | 6155 | Michael Shields |
Updated for the 2007.3 release while maintaining compatibility with prior releases. 2007.3 and later servers might rotate the journal by renaming it rather than copying and truncating it. A renamed journal is now detected by comparing the device and inode returned from statting by the journal's file name and statting by the journal's file descriptor. This algorithm (suggested by J.T. Goldstone; thanks J.T.!) is faster than reopening the journal and seeking if the journal was not rotated (~2.0 seconds vs. ~2.7 seconds for 1,000,000 iterations on my laptop). |
||
#1 | 4839 | Michael Shields | Pushing p4jrep source into the public depot. |