/*
* Copyright (c) 2003 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/evp.h>
#include "applefile.h"
#include "base64.h"
#include "argcargv.h"
#include "cksum.h"
#include "code.h"
#include "pathcmp.h"
#include "largefile.h"
#include "progress.h"
#include "root.h"
void (*logger)( char * ) = NULL;
int cksum = 0;
int verbose = 1;
int amode = R_OK | W_OK;
int case_sensitive = 1;
int checkall = 0;
int checkapplefile = 0;
int updatetran = 1;
char *prefix = NULL;
char *radmind_path = _RADMIND_PATH;
const EVP_MD *md;
extern int showprogress, progress;
extern off_t lsize, total;
extern char *version, *checksumlist;
char prepath[ MAXPATHLEN ] = {0};
/*
* exit codes:
* 0 No changes found, everything okay
* 1 Changes necessary / changes made
* 2 System error
*/
off_t
ckapplefile( char *applefile, int afd )
{
extern struct as_header as_header;
struct as_header header;
struct as_entry as_ents[ 3 ];
int rr;
off_t size = 0;
/* check header */
rr = read( afd, &header, AS_HEADERLEN );
if ( rr < 0 ) {
perror( "read" );
exit( 2 );
}
if ( rr != AS_HEADERLEN ||
memcmp( &as_header, &header, AS_HEADERLEN ) != 0 ) {
goto invalid_applefile;
}
size += rr;
/* check entries */
rr = read( afd, &as_ents, ( 3 * sizeof( struct as_entry )));
if ( rr < 0 ) {
perror( "read" );
exit( 2 );
}
if ( rr != ( 3 * sizeof( struct as_entry ))) {
goto invalid_applefile;
}
size += rr;
as_entry_netswap( &as_ents[ AS_FIE ] );
as_entry_netswap( &as_ents[ AS_RFE ] );
as_entry_netswap( &as_ents[ AS_DFE ] );
/* check entry IDs */
if ( as_ents[ AS_FIE ].ae_id != ASEID_FINFO ||
as_ents[ AS_RFE ].ae_id != ASEID_RFORK ||
as_ents[ AS_DFE ].ae_id != ASEID_DFORK ) {
goto invalid_applefile;
}
/* check offsets */
if ( as_ents[ AS_FIE ].ae_offset !=
( AS_HEADERLEN + ( 3 * sizeof( struct as_entry )))) {
fprintf( stderr, "%s: invalid finder info offset\n", applefile );
return( -1 );
}
if ( as_ents[ AS_RFE ].ae_offset !=
( as_ents[ AS_FIE ].ae_offset +
as_ents[ AS_FIE ].ae_length )) {
fprintf( stderr, "%s: incorrect rsrc fork offset\n", applefile );
return( -1 );
}
if ( as_ents[ AS_DFE ].ae_offset !=
( as_ents[ AS_RFE ].ae_offset +
as_ents[ AS_RFE ].ae_length )) {
fprintf( stderr, "%s: incorrect data fork offset\n", applefile );
return( -1 );
}
/* total sizes as stored in entries */
size += ( as_ents[ AS_FIE ].ae_length +
as_ents[ AS_RFE ].ae_length +
as_ents[ AS_DFE ].ae_length );
return( size );
invalid_applefile:
fprintf( stderr, "%s: invalid applesingle header\n", applefile );
return( -1 );
}
static void
cleanup( int clean, char *path )
{
if ( ! clean || path == NULL ) {
return;
}
if ( unlink( path ) != 0 ) {
fprintf( stderr, "unlink %s: %s\n", path, strerror( errno ));
exit( 2 );
}
}
static int
do_lcksum( char *tpath )
{
int fd, ufd, updateline = 0;
int ucount = 0, len, tac = 0;
int prefixfound = 0;
int remove = 0;
int linenum = 0;
int exitval = 0;
ssize_t bytes = 0;
char *line = NULL;
char *d_path = NULL;
char **targv;
char cwd[ MAXPATHLEN ];
char temp[ MAXPATHLEN ];
char file_root[ MAXPATHLEN ];
char tran_root[ MAXPATHLEN ];
char tran_name[ MAXPATHLEN ];
char tline[ 2 * MAXPATHLEN ];
char path[ 2 * MAXPATHLEN ];
char upath[ 2 * MAXPATHLEN ] = { 0 };
char lcksum[ SZ_BASE64_E( EVP_MAX_MD_SIZE ) ];
FILE *f, *ufs = NULL;
struct stat st;
off_t cksumsize;
if ( getcwd( cwd, MAXPATHLEN ) == NULL ) {
perror( "getcwd" );
exit( 2 );
}
if ( *tpath == '/' ) {
if ( strlen( tpath ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", tpath );
exit( 2 );
}
strcpy( cwd, tpath );
} else {
if ( snprintf( temp, MAXPATHLEN, "%s/%s", cwd, tpath ) >= MAXPATHLEN ) {
fprintf( stderr, "%s/%s: path too long\n", cwd, tpath );
exit( 2 );
}
strcpy( cwd, temp );
}
if ( get_root( radmind_path, cwd, file_root, tran_root, tran_name ) != 0 ) {
exit( 2 );
}
if ( stat( tpath, &st ) != 0 ) {
perror( tpath );
exit( 2 );
}
if ( !S_ISREG( st.st_mode )) {
fprintf( stderr, "%s: not a regular file\n", tpath );
exit( 2 );
}
if ( access( tpath, amode ) != 0 ) {
perror( tpath );
exit( 2 );
}
if (( f = fopen( tpath, "r" )) == NULL ) {
perror( tpath );
exit( 2 );
}
if ( updatetran ) {
memset( upath, 0, MAXPATHLEN );
if ( snprintf( upath, MAXPATHLEN, "%s.%i", tpath, (int)getpid() )
>= MAXPATHLEN ) {
fprintf( stderr, "%s.%i: path too long\n", tpath, (int)getpid() );
}
if ( stat( tpath, &st ) != 0 ) {
perror( tpath );
exit( 2 );
}
/* Open file */
if (( ufd = open( upath, O_WRONLY | O_CREAT | O_EXCL,
st.st_mode )) < 0 ) {
perror( upath );
exit( 2 );
}
if (( ufs = fdopen( ufd, "w" )) == NULL ) {
perror( upath );
cleanup( updatetran, upath );
exit( 2 );
}
}
if ( showprogress ) {
/* calculate the loadset size */
lsize = lcksum_loadsetsize( f, prefix );
/* reset progress variables */
total = 0;
progress = -1;
}
memset( prepath, 0, sizeof( prepath ));
while ( fgets( tline, MAXPATHLEN, f ) != NULL ) {
linenum++;
updateline = 0;
/* Check line length */
len = strlen( tline );
if (( tline[ len - 1 ] ) != '\n' ) {
fprintf( stderr, "%s: %d: line too long\n", tpath, linenum);
exitval = 1;
goto done;
}
/* save transcript line -- must free */
if ( ( line = strdup( tline ) ) == NULL ) {
perror( "strdup" );
cleanup( updatetran, upath );
exit( 2 );
}
tac = acav_parse( NULL, tline, &targv );
/* Skip blank lines and comments */
if (( tac == 0 ) || ( *targv[ 0 ] == '#' )) {
if ( updatetran ) {
fprintf( ufs, "%s", line );
}
goto done;
}
if ( tac == 1 ) {
fprintf( stderr, "line %d: invalid transcript line\n", linenum );
exitval = 1;
goto done;
}
if ( *targv[ 0 ] == '-' ) {
remove = 1;
targv++;
} else {
remove = 0;
}
if (( d_path = decode( targv[ 1 ] )) == NULL ) {
fprintf( stderr, "line %d: path too long\n", linenum );
exitval = 1;
goto done;
}
if ( strlen( d_path ) >= MAXPATHLEN ) {
fprintf( stderr, "line %d: path too long\n", linenum );
exitval = 1;
goto done;
}
strcpy( path, d_path );
/* check to see if file against prefix */
if ( prefix != NULL ) {
if ( strncmp( d_path, prefix, strlen( prefix ))
!= 0 ) {
if ( updatetran ) {
fprintf( ufs, "%s", line );
}
goto done;
}
prefixfound = 1;
}
if ( showprogress && ( tac > 0 && *line != '#' )) {
progressupdate( bytes, d_path );
}
bytes = 0;
/* Check transcript order */
if ( prepath != 0 ) {
if ( pathcasecmp( path, prepath, case_sensitive ) < 0 ) {
if ( updatetran ) {
fprintf( stderr, "line %d: bad sort order\n", linenum );
} else {
fprintf( stderr,
"line %d: bad sort order. Not continuing.\n", linenum );
}
cleanup( updatetran, upath );
exit( 2 );
}
}
if ( strlen( path ) >= MAXPATHLEN ) {
fprintf( stderr, "line %d: path too long\n", linenum );
exitval = 1;
goto done;
}
strcpy( prepath, path );
if ((( *targv[ 0 ] != 'f' ) && ( *targv[ 0 ] != 'a' )) || ( remove )) {
if ( updatetran ) {
fprintf( ufs, "%s", line );
}
bytes += PROGRESSUNIT;
goto done;
}
if ( tac != 8 ) {
fprintf( stderr, "line %d: %d arguments should be 8\n",
linenum, tac );
exitval = 1;
goto done;
}
if ( snprintf( path, MAXPATHLEN, "%s/%s/%s", file_root, tran_name,
d_path ) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%d: %s/%s/%s: path too long\n", linenum,
file_root, tran_name, d_path );
exitval = 1;
goto done;
}
/*
* Since this tool is run on the server, all files can be treated
* as regular files.
*
* HFS+ files saved onto the server are converted to applesingle files.
*
* fsdiff uses do_acksum( ) to calculate the cksum of HFS+ files.
*
* do_acksum( ) creates a cksum for the associated applesingle file.
*/
/* open file here to save us some other open calls */
if (( fd = open( path, O_RDONLY, 0 )) < 0 ) {
fprintf( stderr, "open %s: %s\n", d_path, strerror( errno ));
cleanup( updatetran, upath );
exit( 2 );
}
/* check size */
if ( fstat( fd, &st) != 0 ) {
perror( path );
cleanup( updatetran, upath );
exit( 2 );
}
if (( cksumsize = do_fcksum( fd, lcksum )) < 0 ) {
perror( path );
cleanup( updatetran, upath );
exit( 2 );
}
/* check size */
if ( cksumsize != strtoofft( targv[ 6 ], NULL, 10 )) {
if ( !updatetran ) {
if ( verbose ) printf( "line %d: %s: size wrong\n",
linenum, d_path );
exitval = 1;
} else {
ucount++;
if ( verbose ) printf( "%s: size updated\n", d_path );
}
updateline = 1;
}
bytes += cksumsize;
bytes += PROGRESSUNIT;
/* check cksum */
if ( strcmp( lcksum, targv[ 7 ] ) != 0 ) {
if ( !updatetran ) {
if ( verbose ) printf( "line %d: %s: "
"checksum wrong\n", linenum, d_path );
exitval = 1;
} else {
ucount++;
if ( verbose ) printf( "%s: checksum updated\n", d_path );
}
updateline = 1;
}
if ( *targv[ 0 ] == 'a' && checkapplefile ) {
/* rewind the descriptor */
if ( lseek( fd, 0, SEEK_SET ) < 0 ) {
perror( "lseek" );
exitval = 1;
goto done;
}
if ( ckapplefile( path, fd ) != st.st_size ) {
fprintf( stderr, "%s: corrupted applefile\n", path );
exitval = 1;
goto done;
}
}
if ( close( fd ) != 0 ) {
perror( "close" );
exitval = 1;
goto done;
}
if ( updatetran ) {
if ( updateline ) {
/* Line incorrect */
/* Check to see if checksum is listed in transcript */
if ( strcmp( targv[ 7 ], "-" ) != 0) {
/* use mtime from server */
fprintf( ufs, "%s %-37s %4s %5s %5s %9ld "
"%7" PRIofft "d %s\n",
targv[ 0 ], targv[ 1 ], targv[ 2 ], targv[ 3 ],
targv[ 4 ], st.st_mtime, st.st_size, lcksum );
} else {
/* use mtime from transcript */
fprintf( ufs, "%s %-37s %4s %5s %5s %9s "
"%7" PRIofft "d %s\n",
targv[ 0 ], targv[ 1 ], targv[ 2 ], targv[ 3 ],
targv[ 4 ], targv[ 5 ], st.st_size, lcksum );
}
} else {
/* Line correct */
fprintf( ufs, "%s", line );
}
}
done:
if ( updatetran && ( exitval != 0 )) {
cleanup( updatetran, upath );
exit( 2 );
}
free( line );
}
if ( showprogress ) {
progressupdate( bytes, "" );
}
if ( !prefixfound && prefix != NULL ) {
if ( verbose ) printf( "warning: prefix \"%s\" not found\n", prefix );
}
if ( updatetran ) {
if ( ucount ) {
if ( rename( upath, tpath ) != 0 ) {
fprintf( stderr, "rename %s to %s failed: %s\n", upath, tpath,
strerror( errno ));
exit( 2 );
}
if ( verbose ) printf( "%s: updated\n", tran_name );
return( 1 );
} else {
if ( unlink( upath ) != 0 ) {
perror( upath );
exit( 2 );
}
if ( verbose ) printf( "%s: verified\n", tran_name );
return( 0 );
}
} else {
if ( exitval == 0 ) {
if ( verbose ) printf( "%s: verified\n", tran_name );
return( 0 );
} else {
if ( verbose ) printf( "%s: incorrect\n", tran_name );
return( 1 );
}
}
}
int
main( int argc, char **argv )
{
int c, i, err = 0;
extern int optind;
char *tpath = NULL;
while (( c = getopt( argc, argv, "%Aac:D:iInP:qV" )) != EOF ) {
switch( c ) {
case 'a':
checkall = 1;
break;
case 'A':
checkapplefile = 1;
break;
case '%':
showprogress = 1;
break;
case 'c':
OpenSSL_add_all_digests();
md = EVP_get_digestbyname( optarg );
if ( !md ) {
fprintf( stderr, "%s: unsupported checksum\n", optarg );
exit( 2 );
}
cksum = 1;
break;
case 'i':
setvbuf( stdout, ( char * )NULL, _IOLBF, 0 );
break;
case 'I':
case_sensitive = 0;
break;
case 'D':
radmind_path = optarg;
break;
case 'P':
prefix = optarg;
break;
case 'n':
amode = R_OK;
updatetran = 0;
break;
case 'q':
verbose = 0;
break;
case 'V':
printf( "%s\n", version );
printf( "%s\n", checksumlist );
exit( 0 );
case '?':
err++;
break;
default:
err++;
break;
}
}
if ( cksum == 0 ) {
err++;
}
if ( checkall && updatetran ) {
err++;
}
if ( err || (( argc - optind ) == 0 )) {
fprintf( stderr, "usage: %s [ -%%AiIqV ] ", argv[ 0 ] );
fprintf( stderr, "[ -D path ] " );
fprintf( stderr, "[ -n [ -a ] ] " );
fprintf( stderr, "[ -P prefix ] " );
fprintf( stderr, "-c checksum transcript\n" );
exit( 2 );
}
for ( i = optind; i < argc; i++ ) {
tpath = argv[ i ];
switch ( do_lcksum( tpath )) {
case 2:
exit( 2 );
break;
case 1:
err = 1;
if ( !updatetran ) {
exit( 1 );
}
break;
default:
break;
}
}
exit( err );
}
syntax highlighted by Code2HTML, v. 0.9.1