Logo Search packages:      
Sourcecode: netatalk version File versions

asingle.c

/*
 * $Id: asingle.c,v 1.8.6.1 2003/09/03 20:40:50 didg Exp $
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <sys/types.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/param.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <atalk/adouble.h>
#include <netatalk/endian.h>
#include "asingle.h"
#include "megatron.h"

/*    String used to indicate standard input instead of a disk
      file.  Should be a string not normally used for a file
 */
#ifndef     STDIN
#     define      STDIN "-"
#endif

/*    Yes and no
 */
#define NOWAY           0
#define SURETHANG 1

/*    This structure holds an entry description, consisting of three
      four byte entities.  The first is the Entry ID, the second is
      the File Offset and the third is the Length.
 */


/*    Both input and output routines use this struct and the
      following globals; therefore this module can only be used
      for one of the two functions at a time.
 */
struct single_file_data {
    int                 filed;
    char          path[ MAXPATHLEN + 1];
    struct ad_entry     entry[ ADEID_MAX ];
}           single;

extern char *forkname[];
u_char            header_buf[ AD_HEADER_LEN ];

/* 
 * single_open must be called first.  pass it a filename that is supposed
 * to contain a AppleSingle file.  an single struct will be allocated and
 * somewhat initialized; single_filed is set.
 */

int single_open( singlefile, flags, fh, options )
    char          *singlefile;
    int                 flags, options;
    struct FHeader      *fh;
{
    int                 rc;

    if ( flags == O_RDONLY ) {
      if ( strcmp( singlefile, STDIN ) == 0 ) {
          single.filed = fileno( stdin );
      } else if (( single.filed = open( singlefile, flags )) < 0 ) {
          perror( singlefile );
          return( -1 );
      }
      strncpy( single.path, singlefile, MAXPATHLEN );
#if DEBUG
      fprintf( stderr, "opened %s for read\n", single.path );
#endif /* DEBUG */
      if ((( rc = single_header_test()) > 0 ) && 
            ( single_header_read( fh, rc ) == 0 )) {
          return( 0 );
      }
      single_close( KEEP );
      return( -1 );
    }
    return( 0 );
}

/* 
 * single_close must be called before a second file can be opened using
 * single_open.  Upon successful completion, a value of 0 is returned.  
 * Otherwise, a value of -1 is returned.
 */

int single_close( keepflag )
    int                 keepflag;
{
    if ( keepflag == KEEP ) {
      return( close( single.filed ));
    } else if ( keepflag == TRASH ) {
      if (( strcmp( single.path, STDIN ) != 0 ) && 
            ( unlink( single.path ) < 0 )) {
          perror ( single.path );
      }
      return( 0 );
    } else return( -1 );
}

/* 
 * single_header_read is called by single_open, and before any information
 * can read from the fh substruct.  it must be called before any of the
 * bytes of the other two forks can be read, as well.
 */

int single_header_read( fh, version )
    struct FHeader      *fh;
    int                 version;
{
/*
 * entry_buf is used for reading in entry descriptors, and for reading in
 *    the actual entries of FILEINFO, FINDERINFO, and DATES.
 */
    u_char        entry_buf[ 16 ];
    u_int32_t           entry_id;
    u_int32_t           time_seconds;
    u_short       mask = 0xfcee;
    u_short       num_entries;
    int                 n;
    int                 readlen;
    int                 date_entry = 0;
    off_t         pos;

/*
 * Go through and initialize the array of entry_info structs.  Read in the
 * number of entries, and then read in the info for each entry and save it
 * in the array.
 */

    for ( n = 0 ; n < ADEID_MAX; n++ ) {
      single.entry[ n ].ade_off = 0;
      single.entry[ n ].ade_len = 0;
    }
    memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
    num_entries = ntohs( num_entries );
#if DEBUG >= 2
    fprintf( stderr, "The number of entries is %d\n", num_entries );
#endif /* DEBUG */
    for ( ; num_entries > 0 ; num_entries-- ) {
      if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
            != AD_ENTRY_LEN ) {
          perror( "Premature end of file :" );
          return( -1 );
      }
      memcpy(&entry_id,  entry_buf, sizeof( entry_id ));
      entry_id = ntohl( entry_id );
      memcpy(&single.entry[ entry_id ].ade_off,
             entry_buf + 4,
             sizeof( single.entry[ entry_id ].ade_off ));
      single.entry[ entry_id ].ade_off =
            ntohl( single.entry[ entry_id ].ade_off );
      memcpy(&single.entry[ entry_id ].ade_len,
             entry_buf + 8,
             sizeof( single.entry[ entry_id ].ade_len ));
      single.entry[ entry_id ].ade_len =
            ntohl( single.entry[ entry_id ].ade_len );
#if DEBUG >= 2
      fprintf( stderr, "entry_id\t%d\n", entry_id );
      fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
      fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
#endif /* DEBUG */
    }

/*
 * Now that the entries have been identified, check to make sure
 * it is a Macintosh file if dealing with version two format file.
 */

    if ( version == 1 ) {
      if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
          date_entry = ADEID_FILEI;
    } else if ( version == 2 ) {
      if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
          date_entry = ADEID_FILEDATESI;
    }
#if DEBUG
    fprintf( stderr, "date_entry = %d\n", date_entry );
#endif /* DEBUG */

/*
 * Go through and copy all the information you can get from 
 * the informational entries into the fh struct.  The ENTRYID_DATA
 * must be the last one done, because it leaves the file pointer in
 * the right place for the first read of the data fork.
 */
 
    if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
      fprintf( stderr, "%s has no name for the mac file.\n", single.path );
      return( -1 );
    } else {
      pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
            SEEK_SET );
      readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ? 
            ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
      if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
          perror( "Premature end of file :" );
          return( -1 );
      }
    }
    if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
          ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
      fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
      return( -1 );
    } else {
      pos = lseek( single.filed,
            single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
      if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
            sizeof( entry_buf )) {
          perror( "Premature end of file :" );
          return( -1 );
      }
      memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,     
            sizeof( fh->finder_info.fdType ));
      memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
            sizeof( fh->finder_info.fdCreator ));
      memcpy( &fh->finder_info.fdFlags, entry_buf +  FINDERIOFF_FLAGS,
            sizeof( fh->finder_info.fdFlags ));
      fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
      memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
            sizeof( fh->finder_info.fdLocation ));
      memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
            sizeof( fh->finder_info.fdFldr ));
      fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
      fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);

#if DEBUG
      {
          char          type[5];
          char          creator[5];
 
          strncpy( type, &fh->finder_info.fdType, 4 );
          strncpy( creator, &fh->finder_info.fdCreator, 4 );
          type[4] = creator[4] = '\0';
          fprintf( stderr, "type is %s, creator is %s\n", type, creator );
      }
#endif /* DEBUG */
    }
    if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) || 
          ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
      fh->comment[0] = '\0';
    } else {
      pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
            SEEK_SET );
      readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
            ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
      if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
          perror( "Premature end of file :" );
          return( -1 );
      }
    }
/*
 * If date_entry is 7, we have an AppleSingle version one, do the 
 * appropriate stuff.  If it is 8, we have an AppleSingle version two,
 * do the right thing.  If date_entry is neither, just use the current date.
 * Unless I can't get the current date, in which case use time zero.
 */
    if (( date_entry < 7 ) || ( date_entry > 8 )) {
      if (( time_seconds = time( NULL )) == -1 ) {
          time_seconds = AD_DATE_START;
      } else {
          time_seconds = AD_DATE_FROM_UNIX(time_seconds);
      }
      memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
      memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
      fh->backup_date = AD_DATE_START;
    } else if ( single.entry[ date_entry ].ade_len != 16 ) {
      fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n", 
            single.path );
      return( -1 );
    } else if ( date_entry == ADEID_FILEI ) {
      pos = lseek( single.filed,
            single.entry[ date_entry ].ade_off, SEEK_SET );
      if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
            sizeof( entry_buf )) {
          perror( "Premature end of file :" );
          return( -1 );
      }
      memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
            sizeof( fh->create_date ));
      memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
            sizeof( fh->mod_date ));
      memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
            sizeof(fh->backup_date));
    } else if ( date_entry == ADEID_FILEDATESI ) {
      pos = lseek( single.filed,
            single.entry[ date_entry ].ade_off, SEEK_SET );
      if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
            sizeof( entry_buf )) {
          perror( "Premature end of file :" );
          return( -1 );
      }
      memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
            sizeof( fh->create_date ));
      memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
            sizeof( fh->mod_date ));
      memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
            sizeof(fh->backup_date));
    }
    if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
      fh->forklen[ ADEID_RFORK ] = 0;
    } else {
      fh->forklen[ ADEID_RFORK ] =
            htonl( single.entry[ ADEID_RFORK ].ade_len );
    }
    if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
      fh->forklen[ DATA ] = 0;
    } else {
      fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
      pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
    }

    return( 0 );
}

/*
 * single_header_test is called from single_open.  It checks certain
 * values of the file and determines if the file is an AppleSingle version
 * one file something else, and returns a one, or negative one to indicate
 * file type.
 *
 * The Magic Number of the file, the first four bytes, must be hex
 * 0x00051600.  Bytes 4 through 7 are the version number and must be hex
 * 0x00010000.  Bytes 8 through 23 identify the home file system, and we
 * are only interested in files from Macs.  Therefore these bytes must
 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
 * "Macintosh       " (that is seven blanks of padding).
 */
#define MACINTOSH "Macintosh       "
u_char            sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
                            0, 0, 0, 0, 0, 0, 0, 0 };

int single_header_test(void)
{
    int                 cc;
    u_int32_t           templong;

    cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
    if ( cc < sizeof( header_buf )) {
      perror( "Premature end of file :" );
      return( -1 );
    }

    memcpy( &templong, header_buf, sizeof( templong ));
    if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
      fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
      return( -1 );
    }

    memcpy(&templong,  header_buf +  4, sizeof( templong ));
    templong = ntohl( templong );
    if ( templong == AD_VERSION1 ) {
      cc = 1;
      if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 ) 
            != 0 ) {
          fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n", 
                single.path );
          return( -1 );
      }
    } else if ( templong == AD_VERSION2 ) {
      cc = 2;
      if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
            != 0 ) {
          fprintf( stderr, 
                "Warning:  %s may be a corrupt AppleSingle file.\n",
                single.path );
          return( -1 );
      }
    } else {
      fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
            single.path );
      return( -1 );
    }

    return( cc );
}

/*
 * single_read is called until it returns zero for each fork.  When
 * it returns zero for the first fork, it seeks to the proper place
 * to read in the next, if there is one.  single_read must be called
 * enough times to return zero for each fork and no more.
 *
 */

int single_read( fork, buffer, length )
    int                 fork;
    char          *buffer;
    int                 length;
{
    u_int32_t           entry_id;
    char          *buf_ptr;
    int                 readlen;
    int                 cc = 1;
    off_t         pos;

    switch ( fork ) {
      case DATA :
          entry_id = ADEID_DFORK;
          break;
      case RESOURCE :
          entry_id = ADEID_RFORK;
          break;
      default :
          return( -1 );
          break;
    }

    if ( single.entry[ entry_id ].ade_len < 0 ) {
      fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
      return( single.entry[ entry_id ].ade_len );
    }
    if ( single.entry[ entry_id ].ade_len == 0 ) {
      if ( fork == DATA ) {
          pos = lseek( single.filed,
            single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
      }
      return( 0 );
    }

    if ( single.entry[ entry_id ].ade_len < length ) {
      readlen = single.entry[ entry_id ].ade_len;
    } else {
      readlen = length;
    }

    buf_ptr = buffer;
    while (( readlen > 0 ) && ( cc > 0 )) {
      if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
          readlen -= cc;
          buf_ptr += cc;
      }
    }
    if ( cc >= 0 ) {
      cc = buf_ptr - buffer;
      single.entry[ entry_id ].ade_len -= cc;
    }

    return( cc );
}

Generated by  Doxygen 1.6.0   Back to index