1/* 2 * $Id: macbin.c,v 1.15 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#ifdef HAVE_UNISTD_H 17#include <unistd.h> 18#endif /* HAVE_UNISTD_H */ 19#include <string.h> 20#include <strings.h> 21#include <ctype.h> 22#include <stdio.h> 23#include <time.h> 24 25#include <atalk/adouble.h> 26#include <netatalk/endian.h> 27#include "megatron.h" 28#include "macbin.h" 29#include "updcrc.h" 30 31/* This allows megatron to generate .bin files that won't choke other 32 well-known converter apps. It also makes sure that checksums 33 always match. (RLB) */ 34#define MACBINARY_PLAY_NICE_WITH_OTHERS 35 36/* String used to indicate standard input instead of a disk 37 file. Should be a string not normally used for a file 38 */ 39#ifndef STDIN 40# define STDIN "-" 41#endif /* STDIN */ 42 43/* Yes and no 44 */ 45#define NOWAY 0 46#define SURETHANG 1 47 48/* Size of a macbinary file header 49 */ 50#define HEADBUFSIZ 128 51 52/* Both input and output routines use this struct and the 53 following globals; therefore this module can only be used 54 for one of the two functions at a time. 55 */ 56static struct bin_file_data { 57 u_int32_t forklen[ NUMFORKS ]; 58 char path[ MAXPATHLEN + 1]; 59 int filed; 60 u_short headercrc; 61 time_t gmtoff; /* to convert from/to localtime */ 62} bin; 63 64extern char *forkname[]; 65static u_char head_buf[HEADBUFSIZ]; 66 67/* 68 * bin_open must be called first. pass it a filename that is supposed 69 * to contain a macbinary file. an bin struct will be allocated and 70 * somewhat initialized; bin_filed is set. 71 */ 72 73int bin_open(char *binfile, int flags, struct FHeader *fh, int options) 74{ 75 int maxlen; 76 int rc; 77 time_t t; 78 struct tm *tp; 79 80#if DEBUG 81 fprintf( stderr, "entering bin_open\n" ); 82#endif /* DEBUG */ 83 84 /* call localtime so that we get the timezone offset */ 85 bin.gmtoff = 0; 86#ifndef NO_STRUCT_TM_GMTOFF 87 time(&t); 88 tp = localtime(&t); 89 if (tp) 90 bin.gmtoff = tp->tm_gmtoff; 91#endif /* ! NO_STRUCT_TM_GMTOFF */ 92 93 if ( flags == O_RDONLY ) { /* input */ 94 if ( strcmp( binfile, STDIN ) == 0 ) { 95 bin.filed = fileno( stdin ); 96 } else if (( bin.filed = open( binfile, flags )) < 0 ) { 97 perror( binfile ); 98 return( -1 ); 99 } 100#if DEBUG 101 fprintf( stderr, "opened %s for read\n", binfile ); 102#endif /* DEBUG */ 103 if ((( rc = test_header() ) > 0 ) && 104 ( bin_header_read( fh, rc ) == 0 )) { 105 return( 0 ); 106 } 107 fprintf( stderr, "%s is not a macbinary file.\n", binfile ); 108 return( -1 ); 109 } else { /* output */ 110 if (options & OPTION_STDOUT) 111 bin.filed = fileno(stdout); 112 else { 113 maxlen = sizeof( bin.path ) - 1; 114#if DEBUG 115 fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path )); 116 fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen ); 117#endif /* DEBUG */ 118 strncpy( bin.path, fh->name, maxlen ); 119 strncpy( bin.path, mtoupath( bin.path ), maxlen ); 120 strncat( bin.path, ".bin", maxlen - strlen( bin.path )); 121 if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) { 122 perror( bin.path ); 123 return( -1 ); 124 } 125#if DEBUG 126 fprintf( stderr, "opened %s for write\n", 127 (options & OPTION_STDOUT) ? "(stdout)" : bin.path ); 128#endif /* DEBUG */ 129 } 130 131 if ( bin_header_write( fh ) != 0 ) { 132 bin_close( TRASH ); 133 fprintf( stderr, "%s\n", bin.path ); 134 return( -1 ); 135 } 136 return( 0 ); 137 } 138} 139 140/* 141 * bin_close must be called before a second file can be opened using 142 * bin_open. Upon successful completion, a value of 0 is returned. 143 * Otherwise, a value of -1 is returned. 144 */ 145 146int bin_close(int keepflag) 147{ 148#if DEBUG 149 fprintf( stderr, "entering bin_close\n" ); 150#endif /* DEBUG */ 151 if ( keepflag == KEEP ) { 152 return( close( bin.filed )); 153 } else if ( keepflag == TRASH ) { 154 if (( strcmp( bin.path, STDIN ) != 0 ) && 155 ( unlink( bin.path ) < 0 )) { 156 perror ( bin.path ); 157 } 158 return( 0 ); 159 } else return( -1 ); 160} 161 162/* 163 * bin_read is called until it returns zero for each fork. when it is 164 * and finds that there is zero left to give, it seeks to the position 165 * of the next fork (if there is one ). 166 * bin_read must be called enough times to 167 * return zero and no more than that. 168 */ 169 170ssize_t bin_read( int fork, char *buffer, size_t length) 171{ 172 char *buf_ptr; 173 size_t readlen; 174 ssize_t cc = 1; 175 off_t pos; 176 177#if DEBUG >= 3 178 fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] ); 179 fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] ); 180#endif /* DEBUG >= 3 */ 181 182 if (bin.forklen[fork] > 0x7FFFFFFF) { 183 fprintf(stderr, "This should never happen, dude! fork length == %u\n", bin.forklen[fork]); 184 return -1; 185 } 186 187 if ( bin.forklen[ fork ] == 0 ) { 188 if ( fork == DATA ) { 189 pos = lseek( bin.filed, 0, SEEK_CUR ); 190#if DEBUG 191 fprintf( stderr, "current position is %ld\n", pos ); 192#endif /* DEBUG */ 193 pos %= HEADBUFSIZ; 194 if (pos != 0) { 195 pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR ); 196 } 197#if DEBUG 198 fprintf( stderr, "current position is %ld\n", pos ); 199#endif /* DEBUG */ 200 } 201 return( 0 ); 202 } 203 204 if ( bin.forklen[ fork ] < length ) { 205 readlen = bin.forklen[ fork ]; 206 } else { 207 readlen = length; 208 } 209#if DEBUG >= 3 210 fprintf( stderr, "bin_read: readlen is %d\n", readlen ); 211 fprintf( stderr, "bin_read: cc is %d\n", cc ); 212#endif /* DEBUG >= 3 */ 213 214 buf_ptr = buffer; 215 while (( readlen > 0 ) && ( cc > 0 )) { 216 if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) { 217#if DEBUG >= 3 218 fprintf( stderr, "bin_read: cc is %d\n", cc ); 219#endif /* DEBUG >= 3 */ 220 readlen -= cc; 221 buf_ptr += cc; 222 } 223 } 224 if ( cc >= 0 ) { 225 cc = buf_ptr - buffer; 226 bin.forklen[ fork ] -= cc; 227 } 228 229#if DEBUG >= 3 230 fprintf( stderr, "bin_read: chars read is %d\n", cc ); 231#endif /* DEBUG >= 3 */ 232 return( cc ); 233} 234 235/* 236 * bin_write 237 */ 238 239ssize_t bin_write(int fork, char *buffer, size_t length) 240{ 241 char *buf_ptr; 242 ssize_t writelen; 243 ssize_t cc = 0; 244 off_t pos; 245 u_char padchar = 0x7f; 246 /* Not sure why, but it seems this must be 0x7f to match 247 other converters, not 0. (RLB) */ 248 249#if DEBUG >= 3 250 fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] ); 251 fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] ); 252#endif /* DEBUG >= 3 */ 253 254 if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) { 255 fprintf( stderr, "Forklength error.\n" ); 256 return( -1 ); 257 } 258 259 buf_ptr = (char *)buffer; 260 if ( bin.forklen[ fork ] >= length ) { 261 writelen = length; 262 } else { 263 fprintf( stderr, "Forklength error.\n" ); 264 return( -1 ); 265 } 266 267#if DEBUG >= 3 268 fprintf( stderr, "bin_write: write length is %d\n", writelen ); 269#endif /* DEBUG >= 3 */ 270 271 while (( writelen > 0 ) && ( cc >= 0 )) { 272 cc = write( bin.filed, buf_ptr, writelen ); 273 buf_ptr += cc; 274 writelen -= cc; 275 } 276 if ( cc < 0 ) { 277 perror( "Couldn't write to macbinary file:" ); 278 return( cc ); 279 } 280 281 bin.forklen[fork] -= length; 282 283/* 284 * add the padding at end of data and resource forks 285 */ 286 287 if ( bin.forklen[ fork ] == 0 ) { 288 pos = lseek( bin.filed, 0, SEEK_CUR ); 289#if DEBUG 290 fprintf( stderr, "current position is %ld\n", pos ); 291#endif /* DEBUG */ 292 pos %= HEADBUFSIZ; 293 if (pos != 0) { /* pad only if we need to */ 294 pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR ); 295 if ( write( bin.filed, &padchar, 1 ) != 1 ) { 296 perror( "Couldn't write to macbinary file:" ); 297 return( -1 ); 298 } 299 } 300#if DEBUG 301 fprintf( stderr, "current position is %ld\n", pos ); 302#endif /* DEBUG */ 303 } 304 305#if DEBUG 306 fprintf( stderr, "\n" ); 307#endif /* DEBUG */ 308 309 return( length ); 310} 311 312/* 313 * bin_header_read is called by bin_open, and before any information can 314 * read from the fh substruct. it must be called before any 315 * of the bytes of the other two forks can be read, as well. 316 */ 317 318int bin_header_read(struct FHeader *fh, int revision) 319{ 320 u_short mask; 321 322/* 323 * Set the appropriate finder flags mask for the type of macbinary 324 * file it is, and copy the extra macbinary II stuff from the header. 325 * If it is not a macbinary file revision of I or II, then return 326 * negative. 327 */ 328 329 switch ( revision ) { 330 case 3: 331 case 2 : 332 mask = htons( 0xfcee ); 333 memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 ); 334 break; 335 case 1 : 336 mask = htons( 0xfc00 ); 337 break; 338 default : 339 return( -1 ); 340 break; 341 } 342 343/* 344 * Go through and copy all the stuff you can get from the 345 * MacBinary header into the fh struct. What fun! 346 */ 347 348 memcpy(fh->name, head_buf + 2, head_buf[ 1 ] ); 349 memcpy(&fh->create_date, head_buf + 91, 4 ); 350 fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff; 351 fh->create_date = AD_DATE_FROM_UNIX(fh->create_date); 352 memcpy( &fh->mod_date, head_buf + 95, 4 ); 353 fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff; 354 fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date); 355 fh->backup_date = AD_DATE_START; 356 memcpy( &fh->finder_info, head_buf + 65, 8 ); 357 358#ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */ 359 memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 ); 360 fh->finder_info.fdFlags &= mask; 361#else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */ 362 memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 ); 363#endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */ 364 365 memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 ); 366 memcpy(&fh->finder_info.fdFldr, head_buf + 79, 2 ); 367 memcpy(&fh->forklen[ DATA ], head_buf + 83, 4 ); 368 bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] ); 369 memcpy(&fh->forklen[ RESOURCE ], head_buf + 87, 4 ); 370 bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] ); 371 fh->comment[0] = '\0'; 372 373 if (revision == 3) { 374 fh->finder_xinfo.fdScript = *(head_buf + 106); 375 fh->finder_xinfo.fdXFlags = *(head_buf + 107); 376 } 377 378#if DEBUG >= 5 379 { 380 short flags; 381 long flags_long; 382 383 fprintf( stderr, "Values read by bin_header_read\n" ); 384 fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] ); 385 fprintf( stderr, "file name\t\t%s\n", fh->name ); 386 fprintf( stderr, "get info comment\t%s\n", fh->comment ); 387 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ), 388 &fh->finder_info.fdType ); 389 fprintf( stderr, "creator\t\t\t%.*s\n", 390 sizeof( fh->finder_info.fdCreator ), 391 &fh->finder_info.fdCreator ); 392 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags )); 393 flags = ntohs( flags ); 394 fprintf( stderr, "flags\t\t\t%x\n", flags ); 395 396 /* Show fdLocation too (RLB) */ 397 memcpy( &flags_long, &fh->finder_info.fdLocation, 398 sizeof( flags_long )); 399 flags_long = ntohl( flags_long ); 400 fprintf( stderr, "location flags\t\t%lx\n", flags_long ); 401 402 fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] ); 403 fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] ); 404 fprintf( stderr, "\n" ); 405 } 406#endif /* DEBUG >= 5 */ 407 408 return( 0 ); 409} 410 411/* 412 * bin_header_write is called by bin_open, and relies on information 413 * from the fh substruct. it must be called before any 414 * of the bytes of the other two forks can be written, as well. 415 * bin_header_write and bin_header_read are opposites. 416 */ 417 418int bin_header_write(struct FHeader *fh) 419{ 420 char *write_ptr; 421 u_int32_t t; 422 int wc; 423 int wr; 424 425 memset(head_buf, 0, sizeof( head_buf )); 426 head_buf[ 1 ] = (u_char)strlen( fh->name ); 427 memcpy( head_buf + 2, fh->name, head_buf[ 1 ] ); 428 memcpy( head_buf + 65, &fh->finder_info, 8 ); 429 430#ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */ 431 memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 ); 432#else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */ 433 memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 ); 434#endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */ 435 436 memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 ); 437 memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 ); 438 memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 ); 439 memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 ); 440 t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff; 441 t = MAC_DATE_FROM_UNIX(t); 442 memcpy( head_buf + 91, &t, sizeof(t) ); 443 t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff; 444 t = MAC_DATE_FROM_UNIX(t); 445 memcpy( head_buf + 95, &t, sizeof(t) ); 446 memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1); 447 448 /* macbinary III */ 449 memcpy( head_buf + 102, "mBIN", 4); 450 *(head_buf + 106) = fh->finder_xinfo.fdScript; 451 *(head_buf + 107) = fh->finder_xinfo.fdXFlags; 452 head_buf[ 122 ] = 130; 453 454 head_buf[ 123 ] = 129; 455 456 bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 )); 457 memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc )); 458 459 bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] ); 460 bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] ); 461 462#if DEBUG >= 5 463 { 464 short flags; 465 long flags_long; 466 467 fprintf( stderr, "Values written by bin_header_write\n" ); 468 fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] ); 469 fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] ); 470 fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] ); 471 fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] ); 472 473 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags )); 474 flags = ntohs( flags ); 475 fprintf( stderr, "flags\t\t\t%x\n", flags ); 476 477 /* Show fdLocation too (RLB) */ 478 memcpy( &flags_long, &fh->finder_info.fdLocation, 479 sizeof( flags_long )); 480 flags_long = ntohl( flags_long ); 481 fprintf( stderr, "location flags\t\t%ldx\n", flags_long ); 482 483 fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] ); 484 fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] ); 485 fprintf( stderr, "\n" ); 486 } 487#endif /* DEBUG >= 5 */ 488 489 write_ptr = (char *)head_buf; 490 wc = sizeof( head_buf ); 491 wr = 0; 492 while (( wc > 0 ) && ( wr >= 0 )) { 493 wr = write( bin.filed, write_ptr, wc ); 494 write_ptr += wr; 495 wc -= wr; 496 } 497 if ( wr < 0 ) { 498 perror( "Couldn't write macbinary header:" ); 499 return( wr ); 500 } 501 502 return( 0 ); 503} 504 505/* 506 * test_header is called from bin_open. it checks certain values of 507 * the first 128 bytes, determines if the file is a MacBinary, 508 * MacBinary II, MacBinary III, or non-MacBinary file, and returns a 509 * one, two, three or negative one to indicate the file type. 510 * 511 * If the signature at 102 is equal to "mBIN," then it's a MacBinary 512 * III file. Bytes 0 and 74 must be zero for the file to be any type 513 * of MacBinary. If the crc of bytes 0 through 123 equals the value 514 * at offset 124 then it is a MacBinary II. If not, then if byte 82 515 * is zero, byte 2 is a valid value for a mac filename length (between 516 * one and sixty-three), and bytes 101 through 125 are all zero, then 517 * the file is a MacBinary. 518 * 519 * NOTE: apple's MacBinary II files have a non-zero value at byte 74. 520 * so, the check for byte 74 isn't very useful. 521 */ 522 523int test_header(void) 524{ 525 const char zeros[25] = ""; 526 ssize_t cc; 527 u_short header_crc; 528 u_char namelen; 529 530#if DEBUG 531 fprintf( stderr, "entering test_header\n" ); 532#endif /* DEBUG */ 533 534 cc = read( bin.filed, (char *)head_buf, sizeof( head_buf )); 535 if ( cc < sizeof( head_buf )) { 536 perror( "Premature end of file :" ); 537 return( -1 ); 538 } 539 540#if DEBUG 541 fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" ); 542#endif /* DEBUG */ 543 544 /* check for macbinary III header */ 545 if (memcmp(head_buf + 102, "mBIN", 4) == 0) 546 return 3; 547 548 /* check for macbinary II even if only one of the bytes is zero */ 549 if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) { 550#if DEBUG 551 fprintf( stderr, "byte 0 and 74 are both zero\n" ); 552#endif /* DEBUG */ 553 bin.headercrc = updcrc( (u_short) 0, head_buf, 124 ); 554 memcpy(&header_crc, head_buf + 124, sizeof( header_crc )); 555 header_crc = ntohs( header_crc ); 556 if ( header_crc == bin.headercrc ) { 557 return( 2 ); 558 } 559 560#if DEBUG 561 fprintf( stderr, "header crc didn't pan out\n" ); 562#endif /* DEBUG */ 563 } 564 565 /* now see if we have a macbinary file. */ 566 if ( head_buf[ 82 ] != 0 ) { 567 return( -1 ); 568 } 569 memcpy( &namelen, head_buf + 1, sizeof( namelen )); 570#if DEBUG 571 fprintf( stderr, "name length is %d\n", namelen ); 572#endif /* DEBUG */ 573 if (( namelen < 1 ) || ( namelen > 63 )) { 574 return( -1 ); 575 } 576 577 /* bytes 101 - 125 should be zero */ 578 if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0) 579 return -1; 580 581 /* macbinary forks aren't larger than 0x7FFFFF */ 582 /* we allow forks to be larger, breaking the specs */ 583 memcpy(&cc, head_buf + 83, sizeof(cc)); 584 cc = ntohl(cc); 585 if (cc > 0x7FFFFFFF) 586 return -1; 587 memcpy(&cc, head_buf + 87, sizeof(cc)); 588 cc = ntohl(cc); 589 if (cc > 0x7FFFFFFF) 590 return -1; 591 592 593#if DEBUG 594 fprintf( stderr, "byte 82 is zero and name length is cool\n" ); 595#endif /* DEBUG */ 596 597 return( 1 ); 598} 599