1/* 2 * $Id: asingle.c,v 1.14 2010-01-27 21:27:53 didg Exp $ 3 */ 4 5#ifdef HAVE_CONFIG_H 6#include "config.h" 7#endif /* HAVE_CONFIG_H */ 8 9#include <sys/types.h> 10#include <sys/uio.h> 11#include <sys/time.h> 12#include <sys/param.h> 13#ifdef HAVE_FCNTL_H 14#include <fcntl.h> 15#endif /* HAVE_FCNTL_H */ 16#include <time.h> 17#include <string.h> 18#include <ctype.h> 19#include <stdio.h> 20#ifdef HAVE_UNISTD_H 21#include <unistd.h> 22#endif /* HAVE_UNISTD_H */ 23#include <atalk/adouble.h> 24#include <netatalk/endian.h> 25#include "asingle.h" 26#include "megatron.h" 27 28/* String used to indicate standard input instead of a disk 29 file. Should be a string not normally used for a file 30 */ 31#ifndef STDIN 32# define STDIN "-" 33#endif 34 35/* Yes and no 36 */ 37#define NOWAY 0 38#define SURETHANG 1 39 40/* This structure holds an entry description, consisting of three 41 four byte entities. The first is the Entry ID, the second is 42 the File Offset and the third is the Length. 43 */ 44 45 46/* Both input and output routines use this struct and the 47 following globals; therefore this module can only be used 48 for one of the two functions at a time. 49 */ 50static struct single_file_data { 51 int filed; 52 char path[ MAXPATHLEN + 1]; 53 struct ad_entry entry[ ADEID_MAX ]; 54} single; 55 56extern char *forkname[]; 57static u_char header_buf[ AD_HEADER_LEN ]; 58 59/* 60 * single_open must be called first. pass it a filename that is supposed 61 * to contain a AppleSingle file. an single struct will be allocated and 62 * somewhat initialized; single_filed is set. 63 */ 64 65int single_open(char *singlefile, int flags, struct FHeader *fh, int options _U_) 66{ 67 int rc; 68 69 if ( flags == O_RDONLY ) { 70 if ( strcmp( singlefile, STDIN ) == 0 ) { 71 single.filed = fileno( stdin ); 72 } else if (( single.filed = open( singlefile, flags )) < 0 ) { 73 perror( singlefile ); 74 return( -1 ); 75 } 76 strncpy( single.path, singlefile, MAXPATHLEN ); 77#if DEBUG 78 fprintf( stderr, "opened %s for read\n", single.path ); 79#endif /* DEBUG */ 80 if ((( rc = single_header_test()) > 0 ) && 81 ( single_header_read( fh, rc ) == 0 )) { 82 return( 0 ); 83 } 84 single_close( KEEP ); 85 return( -1 ); 86 } 87 return( 0 ); 88} 89 90/* 91 * single_close must be called before a second file can be opened using 92 * single_open. Upon successful completion, a value of 0 is returned. 93 * Otherwise, a value of -1 is returned. 94 */ 95 96int single_close( int keepflag) 97{ 98 if ( keepflag == KEEP ) { 99 return( close( single.filed )); 100 } else if ( keepflag == TRASH ) { 101 if (( strcmp( single.path, STDIN ) != 0 ) && 102 ( unlink( single.path ) < 0 )) { 103 perror ( single.path ); 104 } 105 return( 0 ); 106 } else return( -1 ); 107} 108 109/* 110 * single_header_read is called by single_open, and before any information 111 * can read from the fh substruct. it must be called before any of the 112 * bytes of the other two forks can be read, as well. 113 */ 114 115int single_header_read( struct FHeader *fh, int version) 116{ 117/* 118 * entry_buf is used for reading in entry descriptors, and for reading in 119 * the actual entries of FILEINFO, FINDERINFO, and DATES. 120 */ 121 u_char entry_buf[ADEDLEN_FINDERI]; 122 u_int32_t entry_id; 123 u_int32_t time_seconds; 124 u_short mask = 0xfcee; 125 u_short num_entries; 126 int n; 127 int readlen; 128 int date_entry = 0; 129 off_t pos; 130 131/* 132 * Go through and initialize the array of entry_info structs. Read in the 133 * number of entries, and then read in the info for each entry and save it 134 * in the array. 135 */ 136 137 for ( n = 0 ; n < ADEID_MAX; n++ ) { 138 single.entry[ n ].ade_off = 0; 139 single.entry[ n ].ade_len = 0; 140 } 141 memcpy( &num_entries, header_buf + 24, sizeof( num_entries )); 142 num_entries = ntohs( num_entries ); 143#if DEBUG >= 2 144 fprintf( stderr, "The number of entries is %d\n", num_entries ); 145#endif /* DEBUG */ 146 for ( ; num_entries > 0 ; num_entries-- ) { 147 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN ) 148 != AD_ENTRY_LEN ) { 149 perror( "Premature end of file :" ); 150 return( -1 ); 151 } 152 memcpy(&entry_id, entry_buf, sizeof( entry_id )); 153 entry_id = ntohl( entry_id ); 154 memcpy(&single.entry[ entry_id ].ade_off, 155 entry_buf + 4, 156 sizeof( single.entry[ entry_id ].ade_off )); 157 single.entry[ entry_id ].ade_off = 158 ntohl( single.entry[ entry_id ].ade_off ); 159 memcpy(&single.entry[ entry_id ].ade_len, 160 entry_buf + 8, 161 sizeof( single.entry[ entry_id ].ade_len )); 162 single.entry[ entry_id ].ade_len = 163 ntohl( single.entry[ entry_id ].ade_len ); 164#if DEBUG >= 2 165 fprintf( stderr, "entry_id\t%d\n", entry_id ); 166 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off ); 167 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len ); 168#endif /* DEBUG */ 169 } 170 171/* 172 * Now that the entries have been identified, check to make sure 173 * it is a Macintosh file if dealing with version two format file. 174 */ 175 176 if ( version == 1 ) { 177 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) 178 date_entry = ADEID_FILEI; 179 } else if ( version == 2 ) { 180 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) 181 date_entry = ADEID_FILEDATESI; 182 } 183#if DEBUG 184 fprintf( stderr, "date_entry = %d\n", date_entry ); 185#endif /* DEBUG */ 186 187/* 188 * Go through and copy all the information you can get from 189 * the informational entries into the fh struct. The ENTRYID_DATA 190 * must be the last one done, because it leaves the file pointer in 191 * the right place for the first read of the data fork. 192 */ 193 194 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) { 195 fprintf( stderr, "%s has no name for the mac file.\n", single.path ); 196 return( -1 ); 197 } else { 198 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off, 199 SEEK_SET ); 200 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ? 201 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len; 202 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) { 203 perror( "Premature end of file :" ); 204 return( -1 ); 205 } 206 } 207 if (( single.entry[ ADEID_FINDERI ].ade_len < ADEDLEN_FINDERI ) || 208 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) { 209 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path ); 210 return( -1 ); 211 } else { 212 pos = lseek( single.filed, 213 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET ); 214 if ( read( single.filed, (char *)entry_buf, ADEDLEN_FINDERI) != ADEDLEN_FINDERI) { 215 perror( "Premature end of file :" ); 216 return( -1 ); 217 } 218 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE, 219 sizeof( fh->finder_info.fdType )); 220 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR, 221 sizeof( fh->finder_info.fdCreator )); 222 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS, 223 sizeof( fh->finder_info.fdFlags )); 224 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask; 225 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC, 226 sizeof( fh->finder_info.fdLocation )); 227 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR, 228 sizeof( fh->finder_info.fdFldr )); 229 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT); 230 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS); 231 232#if DEBUG 233 { 234 char type[5]; 235 char creator[5]; 236 237 strncpy( type, &fh->finder_info.fdType, 4 ); 238 strncpy( creator, &fh->finder_info.fdCreator, 4 ); 239 type[4] = creator[4] = '\0'; 240 fprintf( stderr, "type is %s, creator is %s\n", type, creator ); 241 } 242#endif /* DEBUG */ 243 } 244 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) || 245 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) { 246 fh->comment[0] = '\0'; 247 } else { 248 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off, 249 SEEK_SET ); 250 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT 251 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len; 252 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) { 253 perror( "Premature end of file :" ); 254 return( -1 ); 255 } 256 } 257/* 258 * If date_entry is 7, we have an AppleSingle version one, do the 259 * appropriate stuff. If it is 8, we have an AppleSingle version two, 260 * do the right thing. If date_entry is neither, just use the current date. 261 * Unless I can't get the current date, in which case use time zero. 262 */ 263 if (( date_entry < 7 ) || ( date_entry > 8 )) { 264 if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) { 265 time_seconds = AD_DATE_START; 266 } else { 267 time_seconds = AD_DATE_FROM_UNIX(time_seconds); 268 } 269 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date )); 270 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date )); 271 fh->backup_date = AD_DATE_START; 272 } else if ( single.entry[ date_entry ].ade_len != 16 ) { 273 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n", 274 single.path ); 275 return( -1 ); 276 } else if ( date_entry == ADEID_FILEI ) { 277 pos = lseek( single.filed, 278 single.entry[ date_entry ].ade_off, SEEK_SET ); 279 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) != 280 sizeof( entry_buf )) { 281 perror( "Premature end of file :" ); 282 return( -1 ); 283 } 284 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE, 285 sizeof( fh->create_date )); 286 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY, 287 sizeof( fh->mod_date )); 288 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP, 289 sizeof(fh->backup_date)); 290 } else if ( date_entry == ADEID_FILEDATESI ) { 291 pos = lseek( single.filed, 292 single.entry[ date_entry ].ade_off, SEEK_SET ); 293 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) != 294 sizeof( entry_buf )) { 295 perror( "Premature end of file :" ); 296 return( -1 ); 297 } 298 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE, 299 sizeof( fh->create_date )); 300 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY, 301 sizeof( fh->mod_date )); 302 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP, 303 sizeof(fh->backup_date)); 304 } 305 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) { 306 fh->forklen[RESOURCE] = 0; 307 } else { 308 fh->forklen[RESOURCE] = 309 htonl( single.entry[ ADEID_RFORK ].ade_len ); 310 } 311 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) { 312 fh->forklen[ DATA ] = 0; 313 } else { 314 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len ); 315 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET ); 316 } 317 318 return( 0 ); 319} 320 321/* 322 * single_header_test is called from single_open. It checks certain 323 * values of the file and determines if the file is an AppleSingle version 324 * one file something else, and returns a one, or negative one to indicate 325 * file type. 326 * 327 * The Magic Number of the file, the first four bytes, must be hex 328 * 0x00051600. Bytes 4 through 7 are the version number and must be hex 329 * 0x00010000. Bytes 8 through 23 identify the home file system, and we 330 * are only interested in files from Macs. Therefore these bytes must 331 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII 332 * "Macintosh " (that is seven blanks of padding). 333 */ 334#define MACINTOSH "Macintosh " 335static u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0, 336 0, 0, 0, 0, 0, 0, 0, 0 }; 337 338int single_header_test(void) 339{ 340 ssize_t cc; 341 u_int32_t templong; 342 343 cc = read( single.filed, (char *)header_buf, sizeof( header_buf )); 344 if ( cc < (ssize_t)sizeof( header_buf )) { 345 perror( "Premature end of file :" ); 346 return( -1 ); 347 } 348 349 memcpy( &templong, header_buf, sizeof( templong )); 350 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) { 351 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path ); 352 return( -1 ); 353 } 354 355 memcpy(&templong, header_buf + 4, sizeof( templong )); 356 templong = ntohl( templong ); 357 if ( templong == AD_VERSION1 ) { 358 cc = 1; 359 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 ) 360 != 0 ) { 361 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n", 362 single.path ); 363 return( -1 ); 364 } 365 } else if ( templong == AD_VERSION2 ) { 366 cc = 2; 367 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls )) 368 != 0 ) { 369 fprintf( stderr, 370 "Warning: %s may be a corrupt AppleSingle file.\n", 371 single.path ); 372 return( -1 ); 373 } 374 } else { 375 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n", 376 single.path ); 377 return( -1 ); 378 } 379 380 return( cc ); 381} 382 383/* 384 * single_read is called until it returns zero for each fork. When 385 * it returns zero for the first fork, it seeks to the proper place 386 * to read in the next, if there is one. single_read must be called 387 * enough times to return zero for each fork and no more. 388 * 389 */ 390 391ssize_t single_read( int fork, char *buffer, size_t length) 392{ 393 u_int32_t entry_id; 394 char *buf_ptr; 395 size_t readlen; 396 ssize_t cc = 1; 397 off_t pos; 398 399 switch ( fork ) { 400 case DATA : 401 entry_id = ADEID_DFORK; 402 break; 403 case RESOURCE : 404 entry_id = ADEID_RFORK; 405 break; 406 default : 407 return( -1 ); 408 break; 409 } 410 411 if (single.entry[entry_id].ade_len > 0x7FFFFFFF) { 412 fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len); 413 return -1; 414 } 415 if ( single.entry[ entry_id ].ade_len == 0 ) { 416 if ( fork == DATA ) { 417 pos = lseek( single.filed, 418 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET ); 419 } 420 return( 0 ); 421 } 422 423 if ( single.entry[ entry_id ].ade_len < length ) { 424 readlen = single.entry[ entry_id ].ade_len; 425 } else { 426 readlen = length; 427 } 428 429 buf_ptr = buffer; 430 while (( readlen > 0 ) && ( cc > 0 )) { 431 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) { 432 readlen -= cc; 433 buf_ptr += cc; 434 } 435 } 436 if ( cc >= 0 ) { 437 cc = buf_ptr - buffer; 438 single.entry[ entry_id ].ade_len -= cc; 439 } 440 441 return( cc ); 442} 443