#ident "$Header: /proj/irix6.5.7m/isms/eoe/cmd/xfs/dump/treecmp/RCS/main.c,v 1.2 1996/04/23 01:21:15 doucette Exp $" #include #include #include #include #include #include #include #include #include #include #include #include #include "types.h" #include "path.h" #define CMPBUFSZ ( 4 * 4096 ) struct dent { struct dent *d_next; char d_name[ 1 ]; }; typedef struct dent dent_t; struct dentbuf { size_t db_cnt; dent_t **db_array; dent_t *db_list; }; typedef struct dentbuf dentbuf_t; struct dentbuf_iter { size_t dbi_remaining; dent_t **dbi_nextp; }; typedef struct dentbuf_iter dentbuf_iter_t; #define MLOG_NORMAL 0 #define MLOG_VERBOSE 1 #define MLOG_TRACE 2 #define PATH_SZ ( MAXPATHLEN * 32 * 8 ) struct path { size_t p_sz; size_t p_len; char p_head[ PATH_SZ ]; char *p_next; }; typedef struct path path_t; typedef u_int32_t sum_t; static char *progname; static intgen_t mlog_level = MLOG_NORMAL; static bool_t cmp_sums = BOOL_FALSE; static bool_t cmp_bytes = BOOL_FALSE; static char *aroot; static char *broot; static path_t *apathp; static path_t *bpathp; static path_t *rpathp; static intgen_t dircmp( intgen_t ); static dentbuf_t *dentbuf_create( char * ); static intgen_t dentcmp( const void *, const void * ); static dentbuf_iter_t *dentbuf_iter_create( dentbuf_t * ); static char *dentbuf_iter_next( dentbuf_iter_t * ); static void dentbuf_iter_destroy( dentbuf_iter_t * ); static void dentbuf_destroy( dentbuf_t * ); static path_t *path_create( char *base ); static void path_append( path_t *pathp, char *name ); static void path_peel( path_t *pathp ); static void path_print( path_t *pathp, intgen_t level, char *fmt, ... ); static intgen_t statcmp( stat64_t *, stat64_t *, bool_t *, bool_t * ); static sum_t sum( intgen_t, bool_t * ); static void cmp( int fda, int fdb ); static int cmpbuf( char *bufa, char *bufb, int cnt ); static void mlog( intgen_t, char *, ... ); static char * ctimennl( const time_t * ); int main( intgen_t argc, char *argv[ ] ) { intgen_t c; char *homedir; intgen_t rval; progname = argv[ 0 ]; if ( argc <= 1 ) { mlog( MLOG_NORMAL, "usage: %s [-v <0-3>] [-s (cmp sums)] [-c (cmp bytes)] " " \n", progname ); return 0; } while ( ( c = getopt( argc, argv, "v:sc" )) != EOF ) { switch ( c ) { case 'v': if ( ! optarg || optarg[ 0 ] == '-' ) { fprintf( stderr, "%s: -%c argument missing\n", progname, optopt ); return 1; } mlog_level = atoi( optarg ); break; case 's': cmp_sums = BOOL_TRUE; break; case 'c': cmp_bytes = BOOL_TRUE; break; } } homedir = getcwd( 0, MAXPATHLEN ); assert( homedir ); aroot = path_reltoabs( argv[ optind ], homedir ); broot = path_reltoabs( argv[ optind + 1 ], homedir ); mlog( MLOG_VERBOSE, "comparing %s to %s\n", aroot, broot ); apathp = path_create( aroot ); bpathp = path_create( broot ); rpathp = path_create( "." ); rval = dircmp( 0 ); return (int)rval; } static intgen_t dircmp( intgen_t l ) { stat64_t abuf; stat64_t bbuf; bool_t isdir; bool_t isreg; intgen_t afd; intgen_t bfd; dentbuf_t *dap; dentbuf_t *dbp; char *ca; char *cb; dentbuf_iter_t *diap; dentbuf_iter_t *dibp; /* REFERENCED */ intgen_t rval; if ( l > 0 ) { path_print( rpathp, MLOG_TRACE, "checking" ); } rval = lstat64( apathp->p_head, &abuf ); assert( ! rval ); rval = lstat64( bpathp->p_head, &bbuf ); assert( ! rval ); if ( l > 0 ) { rval = statcmp( &abuf, &bbuf, &isdir, &isreg ); assert( ! rval ); } else { isreg = BOOL_FALSE; isdir = BOOL_TRUE; } if ( ! isdir && ( ! ( cmp_sums || cmp_bytes ) || ! isreg )) { return 0; } afd = open( apathp->p_head, O_RDONLY ); assert( afd >= 0 ); bfd = open( bpathp->p_head, O_RDONLY ); assert( bfd >= 0 ); if ( isreg ) { if ( cmp_bytes ) { cmp( afd, bfd ); } else if ( cmp_bytes ) { sum_t suma; sum_t sumb; bool_t readerror; readerror = 0; suma = sum( afd, &readerror ); if ( readerror ) { path_print( apathp, MLOG_NORMAL, "read error" ); } readerror = 0; sumb = sum( bfd, &readerror ); if ( readerror ) { path_print( bpathp, MLOG_NORMAL, "read error" ); } if ( suma != sumb ) { path_print( rpathp, MLOG_NORMAL, "checksums differ" ); } } close( afd ); close( bfd ); return 0; } dap = dentbuf_create( apathp->p_head ); dbp = dentbuf_create( bpathp->p_head ); diap = dentbuf_iter_create( dap ); dibp = dentbuf_iter_create( dbp ); ca = dentbuf_iter_next( diap ); cb = dentbuf_iter_next( dibp ); for ( ; ; ) { intgen_t diff; if ( ! ca && ! cb ) { break; } if ( ca && ! cb ) { path_append( bpathp, ca ); path_print( bpathp, MLOG_NORMAL, "missing" ); path_peel( bpathp ); ca = dentbuf_iter_next( diap ); continue; } if ( ! ca && cb ) { path_append( apathp, cb ); path_print( apathp, MLOG_NORMAL, "extra" ); path_peel( apathp ); cb = dentbuf_iter_next( dibp ); continue; } diff = strcmp( ca, cb ); if ( diff < 0 ) { path_append( bpathp, ca ); path_print( bpathp, MLOG_NORMAL, "missing" ); path_peel( bpathp ); ca = dentbuf_iter_next( diap ); continue; } if ( diff > 0 ) { path_append( apathp, cb ); path_print( apathp, MLOG_NORMAL, "extra" ); path_peel( apathp ); cb = dentbuf_iter_next( dibp ); continue; } path_append( apathp, ca ); path_append( bpathp, cb ); path_append( rpathp, ca ); ( void )dircmp( l + 1 ); path_peel( apathp ); path_peel( bpathp ); path_peel( rpathp ); ca = dentbuf_iter_next( diap ); cb = dentbuf_iter_next( dibp ); } dentbuf_iter_destroy( dibp ); dentbuf_iter_destroy( diap ); dentbuf_destroy( dap ); dentbuf_destroy( dbp ); close( bfd ); close( afd ); return 0; } static dentbuf_t * dentbuf_create( char *dirname ) { DIR *dirp; dirent_t *dep; dentbuf_t *dbp; dent_t *dp; dent_t *rootp; dent_t *lastp; size_t cnt; dent_t **array; dent_t **ap; dbp = ( dentbuf_t * )malloc( sizeof( dentbuf_t )); assert( dbp ); dirp = opendir( dirname ); assert( dirp ); cnt = 0; rootp = 0; lastp = 0; while ( ( dep = readdir( dirp )) != NULL ) { if ( *( dep->d_name + 0 ) == '.' && ( *( dep->d_name + 1 ) == 0 || ( *( dep->d_name + 1 ) == '.' && *( dep->d_name + 2 ) == 0 ))) { continue; } dp = ( dent_t * )malloc( sizeof( dent_t ) + strlen( dep->d_name )); assert( dp ); dp->d_next = 0; strcpy( dp->d_name, dep->d_name ); if ( ! rootp ) { rootp = dp; lastp = dp; } else { lastp->d_next = dp; lastp = dp; } cnt++; } array = ( dent_t ** )calloc( cnt, sizeof( dent_t * )); assert( array ); dbp->db_cnt = cnt; for ( dp = rootp, ap = &array[ 0 ] ; cnt > 0 ; dp = dp->d_next, ap++, cnt-- ) { *ap = dp; } qsort( ( void * )array, dbp->db_cnt, sizeof( dent_t * ), dentcmp ); dbp->db_array = array; dbp->db_list = rootp; closedir( dirp ); return dbp; } static intgen_t dentcmp( const void *ap, const void *bp ) { dent_t *adp = *( dent_t ** )ap; dent_t *bdp = *( dent_t ** )bp; return strcmp( adp->d_name, bdp->d_name ); } static void dentbuf_destroy( dentbuf_t *dbp ) { dent_t *dp; dp = dbp->db_list; while ( dp ) { register dent_t *olddp = dp; dp = dp->d_next; free( ( void * )olddp ); } free( ( void * )dbp->db_array ); free( ( void * )dbp ); } static dentbuf_iter_t * dentbuf_iter_create( dentbuf_t *dbp ) { dentbuf_iter_t *dip; dip = ( dentbuf_iter_t * )malloc( sizeof( dentbuf_iter_t )); assert( dip ); dip->dbi_remaining = dbp->db_cnt; dip->dbi_nextp = &dbp->db_array[ 0 ]; return dip; } static char * dentbuf_iter_next( dentbuf_iter_t *dip ) { if ( dip->dbi_remaining > 0 ) { dent_t *dp; dip->dbi_remaining--; dp = *dip->dbi_nextp; dip->dbi_nextp++; return dp->d_name; } else { return 0; } } static void dentbuf_iter_destroy( dentbuf_iter_t *dip ) { free( ( void * )dip ); } static intgen_t statcmp( stat64_t * abufp, stat64_t *bbufp, bool_t *isdirp, bool_t *isregp ) { bool_t aisdir; bool_t bisdir; bool_t aissym; bool_t bissym; bool_t bothsym; aisdir = ( ( abufp->st_mode & S_IFMT ) == S_IFDIR ) ? BOOL_TRUE : BOOL_FALSE; bisdir = ( ( bbufp->st_mode & S_IFMT ) == S_IFDIR ) ? BOOL_TRUE : BOOL_FALSE; if ( aisdir ^ bisdir ) { if ( bisdir ) { path_print( bpathp, MLOG_NORMAL, "changed to dir" ); } else { path_print( bpathp, MLOG_NORMAL, "changed to non-dir" ); } *isdirp = BOOL_FALSE; return 0; } aissym = ( ( abufp->st_mode & S_IFMT ) == S_IFLNK ) ? BOOL_TRUE : BOOL_FALSE; bissym = ( ( bbufp->st_mode & S_IFMT ) == S_IFLNK ) ? BOOL_TRUE : BOOL_FALSE; bothsym = aissym && bissym; if ( ! bothsym && abufp->st_mode != bbufp->st_mode ) { path_print( bpathp, MLOG_NORMAL, "mode changed from %04x to %04x", abufp->st_mode, bbufp->st_mode ); } if ( ! bothsym && abufp->st_mtime != bbufp->st_mtime ) { char ctimebuf[ 27 ]; strcpy( ctimebuf, ctimennl( &abufp->st_mtime )); assert( strlen( ctimebuf ) < sizeof( ctimebuf )); path_print( bpathp, MLOG_NORMAL, "mtime changed from %s to %s", ctimebuf, ctimennl( &bbufp->st_mtime )); } if ( abufp->st_uid != bbufp->st_uid ) { path_print( bpathp, MLOG_NORMAL, "uid changed from %d to %d", abufp->st_uid, bbufp->st_uid ); } if ( abufp->st_gid != bbufp->st_gid ) { path_print( bpathp, MLOG_NORMAL, "gid changed from %d to %d", abufp->st_gid, bbufp->st_gid ); } if ( ! aisdir && abufp->st_size != bbufp->st_size ) { path_print( bpathp, MLOG_NORMAL, "size changed from %lld to %lld", abufp->st_size, bbufp->st_size ); } if ( abufp->st_mode == bbufp->st_mode && ( abufp->st_mode & S_IFMT ) == S_IFREG ) { *isregp = BOOL_TRUE; } else { *isregp = BOOL_FALSE; } *isdirp = aisdir; return 0; } static sum_t sum( intgen_t fd, bool_t *readerrorp ) { char buf[ PGSZ ]; /* PGSZ % sizeof( sum_t ) MUST be zero ! */ intgen_t nread; sum_t accum; accum = 0; while ( ( nread = read( fd, ( void * )buf, sizeof( buf ))) > 0 ) { sum_t *sump = ( sum_t * )buf; size_t ix; size_t sumread = ( ( size_t )nread + sizeof( sum_t ) - 1 ) / sizeof( sum_t ); size_t remcnt = ( size_t )nread % sizeof( sum_t ); if ( remcnt ) { remcnt = sizeof( sum_t ) - remcnt; } for ( ix = ( size_t )nread ; ix < ( size_t )nread + remcnt ; ix++ ) { buf[ ix ] = 0; } for ( ; sumread ; sump++, sumread-- ) { accum += *sump; } } if ( nread < 0 ) { *readerrorp = BOOL_TRUE; } else { *readerrorp = BOOL_FALSE; } return accum; } static void mlog( intgen_t level, char *fmt, ... ) { va_list args; if ( level > mlog_level ) { return; } va_start( args, fmt ); vfprintf( stdout, fmt, args ); va_end( args ); } static path_t * path_create( char *base ) { path_t *pathp = ( path_t * )calloc( 1, sizeof( path_t )); size_t baselen = strlen( base ); assert( pathp ); pathp->p_sz = PATH_SZ; pathp->p_len = 0; pathp->p_next = pathp->p_head; assert( pathp->p_len + baselen < pathp->p_sz ); strcpy( pathp->p_next, base ); pathp->p_next += baselen; pathp->p_len += baselen; return pathp; } static void path_append( path_t *pathp, char *name ) { size_t namelen = strlen( name ); assert( pathp->p_len + namelen + 1 < pathp->p_sz ); *pathp->p_next = '/'; pathp->p_next++; pathp->p_len++; strcpy( pathp->p_next, name ); pathp->p_next += namelen; pathp->p_len += namelen; } static void path_peel( path_t *pathp ) { char *tp; size_t namelen; for ( tp = pathp->p_next - 1, namelen = 0 ; tp >= pathp->p_head ; tp--, namelen++ ) { if ( *tp == '/' ) { break; } } assert( tp >= pathp->p_head ); *tp = 0; pathp->p_len -= namelen + 1; pathp->p_next -= namelen + 1; } static void path_print( path_t *pathp, intgen_t level, char *fmt, ... ) { va_list args; if ( level > mlog_level ) { return; } fputs( pathp->p_head, stderr ); fputs( ": ", stderr ); va_start( args, fmt ); vfprintf( stderr, fmt, args ); va_end( args ); fputc( '\n', stderr ); } static char * ctimennl( const time_t *clockp ) { char *p = ctime( clockp ); if ( p && strlen( p ) > 0 ) { p[ strlen( p ) - 1 ] = 0; } return p; } static void cmp( int fda, int fdb ) { char bufa[ CMPBUFSZ ]; char bufb[ CMPBUFSZ ]; int cnt; for ( cnt = 0 ; ; cnt += ( int )sizeof( bufa )) { int nreada; int nreadb; int bufcnt; nreada = read( fda, bufa, sizeof( bufa )); if ( nreada < 0 ) { path_print( apathp, MLOG_NORMAL, "read error" ); return; } nreadb = read( fdb, bufb, sizeof( bufb )); if ( nreadb < 0 ) { path_print( bpathp, MLOG_NORMAL, "read error" ); return; } if ( nreada < nreadb ) { /* path_print( bpathp, MLOG_NORMAL, "longer" ); */ return; } if ( nreada > nreadb ) { /* path_print( bpathp, MLOG_NORMAL, "shorter" ); */ return; } bufcnt = cmpbuf( bufa, bufb, nreada ); if ( bufcnt != nreada ) { path_print( rpathp, MLOG_NORMAL, "differ at byte %d\n", cnt + bufcnt ); return; } if ( nreada < ( int )sizeof( bufa )) { return; } } } static int cmpbuf( char *bufa, char *bufb, int cnt ) { int c; for ( c = 0 ; c < cnt ; c++ ) { if ( *bufa++ != *bufb++ ) { break; } } return c; }