2 * $Id: macbin.c,v 1.14 2009-10-14 01:38:28 didg Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
15 #endif /* HAVE_FCNTL_H */
18 #endif /* HAVE_UNISTD_H */
25 #include <atalk/adouble.h>
26 #include <netatalk/endian.h>
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
36 /* String used to indicate standard input instead of a disk
37 file. Should be a string not normally used for a file
48 /* Size of a macbinary file header
50 #define HEADBUFSIZ 128
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.
56 static struct bin_file_data {
57 u_int32_t forklen[ NUMFORKS ];
58 char path[ MAXPATHLEN + 1];
61 time_t gmtoff; /* to convert from/to localtime */
64 extern char *forkname[];
65 static u_char head_buf[HEADBUFSIZ];
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.
73 int bin_open(char *binfile, int flags, struct FHeader *fh, int options)
81 fprintf( stderr, "entering bin_open\n" );
84 /* call localtime so that we get the timezone offset */
86 #ifndef NO_STRUCT_TM_GMTOFF
90 bin.gmtoff = tp->tm_gmtoff;
91 #endif /* ! NO_STRUCT_TM_GMTOFF */
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 ) {
101 fprintf( stderr, "opened %s for read\n", binfile );
103 if ((( rc = test_header() ) > 0 ) &&
104 ( bin_header_read( fh, rc ) == 0 )) {
107 fprintf( stderr, "%s is not a macbinary file.\n", binfile );
109 } else { /* output */
110 if (options & OPTION_STDOUT)
111 bin.filed = fileno(stdout);
113 maxlen = sizeof( bin.path ) - 1;
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 );
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 ) {
126 fprintf( stderr, "opened %s for write\n",
127 (options & OPTION_STDOUT) ? "(stdout)" : bin.path );
131 if ( bin_header_write( fh ) != 0 ) {
133 fprintf( stderr, "%s\n", bin.path );
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.
146 int bin_close(int keepflag)
149 fprintf( stderr, "entering bin_close\n" );
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 )) {
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.
170 int bin_read( int fork, char *buffer, int length)
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 */
182 if (bin.forklen[fork] > length) {
183 fprintf(stderr, "This should never happen, dude! length %d, fork length == %u\n", length, bin.forklen[fork]);
184 return bin.forklen[fork];
187 if ( bin.forklen[ fork ] == 0 ) {
188 if ( fork == DATA ) {
189 pos = lseek( bin.filed, 0, SEEK_CUR );
191 fprintf( stderr, "current position is %ld\n", pos );
195 pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
198 fprintf( stderr, "current position is %ld\n", pos );
204 if ( bin.forklen[ fork ] < length ) {
205 readlen = bin.forklen[ fork ];
210 fprintf( stderr, "bin_read: readlen is %d\n", readlen );
211 fprintf( stderr, "bin_read: cc is %d\n", cc );
212 #endif /* DEBUG >= 3 */
215 while (( readlen > 0 ) && ( cc > 0 )) {
216 if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
218 fprintf( stderr, "bin_read: cc is %d\n", cc );
219 #endif /* DEBUG >= 3 */
225 cc = buf_ptr - buffer;
226 bin.forklen[ fork ] -= cc;
230 fprintf( stderr, "bin_read: chars read is %d\n", cc );
231 #endif /* DEBUG >= 3 */
239 int bin_write(int fork, char *buffer, int length)
245 u_char padchar = 0x7f;
246 /* Not sure why, but it seems this must be 0x7f to match
247 other converters, not 0. (RLB) */
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 */
254 if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
255 fprintf( stderr, "Forklength error.\n" );
259 buf_ptr = (char *)buffer;
260 if ( bin.forklen[ fork ] >= length ) {
263 fprintf( stderr, "Forklength error.\n" );
268 fprintf( stderr, "bin_write: write length is %d\n", writelen );
269 #endif /* DEBUG >= 3 */
271 while (( writelen > 0 ) && ( cc >= 0 )) {
272 cc = write( bin.filed, buf_ptr, writelen );
277 perror( "Couldn't write to macbinary file:" );
281 if (length > bin.forklen[fork]) {
282 fprintf(stderr, "This should never happen, dude! length %d, fork length %u\n", length, bin.forklen[fork]);
283 return bin.forklen[fork];
286 bin.forklen[fork] -= length;
289 * add the padding at end of data and resource forks
292 if ( bin.forklen[ fork ] == 0 ) {
293 pos = lseek( bin.filed, 0, SEEK_CUR );
295 fprintf( stderr, "current position is %ld\n", pos );
298 if (pos != 0) { /* pad only if we need to */
299 pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
300 if ( write( bin.filed, &padchar, 1 ) != 1 ) {
301 perror( "Couldn't write to macbinary file:" );
306 fprintf( stderr, "current position is %ld\n", pos );
311 fprintf( stderr, "\n" );
318 * bin_header_read is called by bin_open, and before any information can
319 * read from the fh substruct. it must be called before any
320 * of the bytes of the other two forks can be read, as well.
323 int bin_header_read(struct FHeader *fh, int revision)
328 * Set the appropriate finder flags mask for the type of macbinary
329 * file it is, and copy the extra macbinary II stuff from the header.
330 * If it is not a macbinary file revision of I or II, then return
334 switch ( revision ) {
337 mask = htons( 0xfcee );
338 memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
341 mask = htons( 0xfc00 );
349 * Go through and copy all the stuff you can get from the
350 * MacBinary header into the fh struct. What fun!
353 memcpy(fh->name, head_buf + 2, head_buf[ 1 ] );
354 memcpy(&fh->create_date, head_buf + 91, 4 );
355 fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
356 fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
357 memcpy( &fh->mod_date, head_buf + 95, 4 );
358 fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
359 fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
360 fh->backup_date = AD_DATE_START;
361 memcpy( &fh->finder_info, head_buf + 65, 8 );
363 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
364 memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
365 fh->finder_info.fdFlags &= mask;
366 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
367 memcpy( &fh->finder_info.fdFlags, head_buf + 73, 2 );
368 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
370 memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
371 memcpy(&fh->finder_info.fdFldr, head_buf + 79, 2 );
372 memcpy(&fh->forklen[ DATA ], head_buf + 83, 4 );
373 bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
374 memcpy(&fh->forklen[ RESOURCE ], head_buf + 87, 4 );
375 bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
376 fh->comment[0] = '\0';
379 fh->finder_xinfo.fdScript = *(head_buf + 106);
380 fh->finder_xinfo.fdXFlags = *(head_buf + 107);
388 fprintf( stderr, "Values read by bin_header_read\n" );
389 fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
390 fprintf( stderr, "file name\t\t%s\n", fh->name );
391 fprintf( stderr, "get info comment\t%s\n", fh->comment );
392 fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
393 &fh->finder_info.fdType );
394 fprintf( stderr, "creator\t\t\t%.*s\n",
395 sizeof( fh->finder_info.fdCreator ),
396 &fh->finder_info.fdCreator );
397 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
398 flags = ntohs( flags );
399 fprintf( stderr, "flags\t\t\t%x\n", flags );
401 /* Show fdLocation too (RLB) */
402 memcpy( &flags_long, &fh->finder_info.fdLocation,
403 sizeof( flags_long ));
404 flags_long = ntohl( flags_long );
405 fprintf( stderr, "location flags\t\t%lx\n", flags_long );
407 fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
408 fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
409 fprintf( stderr, "\n" );
411 #endif /* DEBUG >= 5 */
417 * bin_header_write is called by bin_open, and relies on information
418 * from the fh substruct. it must be called before any
419 * of the bytes of the other two forks can be written, as well.
420 * bin_header_write and bin_header_read are opposites.
423 int bin_header_write(struct FHeader *fh)
430 memset(head_buf, 0, sizeof( head_buf ));
431 head_buf[ 1 ] = (u_char)strlen( fh->name );
432 memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
433 memcpy( head_buf + 65, &fh->finder_info, 8 );
435 #ifndef MACBINARY_PLAY_NICE_WITH_OTHERS /* (RLB) */
436 memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1 );
437 #else /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
438 memcpy( head_buf + 73, &fh->finder_info.fdFlags, 2 );
439 #endif /* ! MACBINARY_PLAY_NICE_WITH_OTHERS */
441 memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
442 memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
443 memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
444 memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
445 t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
446 t = MAC_DATE_FROM_UNIX(t);
447 memcpy( head_buf + 91, &t, sizeof(t) );
448 t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
449 t = MAC_DATE_FROM_UNIX(t);
450 memcpy( head_buf + 95, &t, sizeof(t) );
451 memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
454 memcpy( head_buf + 102, "mBIN", 4);
455 *(head_buf + 106) = fh->finder_xinfo.fdScript;
456 *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
457 head_buf[ 122 ] = 130;
459 head_buf[ 123 ] = 129;
461 bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
462 memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
464 bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
465 bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
472 fprintf( stderr, "Values written by bin_header_write\n" );
473 fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
474 fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
475 fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
476 fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
478 memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
479 flags = ntohs( flags );
480 fprintf( stderr, "flags\t\t\t%x\n", flags );
482 /* Show fdLocation too (RLB) */
483 memcpy( &flags_long, &fh->finder_info.fdLocation,
484 sizeof( flags_long ));
485 flags_long = ntohl( flags_long );
486 fprintf( stderr, "location flags\t\t%ldx\n", flags_long );
488 fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
489 fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
490 fprintf( stderr, "\n" );
492 #endif /* DEBUG >= 5 */
494 write_ptr = (char *)head_buf;
495 wc = sizeof( head_buf );
497 while (( wc > 0 ) && ( wr >= 0 )) {
498 wr = write( bin.filed, write_ptr, wc );
503 perror( "Couldn't write macbinary header:" );
511 * test_header is called from bin_open. it checks certain values of
512 * the first 128 bytes, determines if the file is a MacBinary,
513 * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
514 * one, two, three or negative one to indicate the file type.
516 * If the signature at 102 is equal to "mBIN," then it's a MacBinary
517 * III file. Bytes 0 and 74 must be zero for the file to be any type
518 * of MacBinary. If the crc of bytes 0 through 123 equals the value
519 * at offset 124 then it is a MacBinary II. If not, then if byte 82
520 * is zero, byte 2 is a valid value for a mac filename length (between
521 * one and sixty-three), and bytes 101 through 125 are all zero, then
522 * the file is a MacBinary.
524 * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
525 * so, the check for byte 74 isn't very useful.
528 int test_header(void)
530 const char zeros[25] = "";
536 fprintf( stderr, "entering test_header\n" );
539 cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
540 if ( cc < sizeof( head_buf )) {
541 perror( "Premature end of file :" );
546 fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
549 /* check for macbinary III header */
550 if (memcmp(head_buf + 102, "mBIN", 4) == 0)
553 /* check for macbinary II even if only one of the bytes is zero */
554 if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
556 fprintf( stderr, "byte 0 and 74 are both zero\n" );
558 bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
559 memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
560 header_crc = ntohs( header_crc );
561 if ( header_crc == bin.headercrc ) {
566 fprintf( stderr, "header crc didn't pan out\n" );
570 /* now see if we have a macbinary file. */
571 if ( head_buf[ 82 ] != 0 ) {
574 memcpy( &namelen, head_buf + 1, sizeof( namelen ));
576 fprintf( stderr, "name length is %d\n", namelen );
578 if (( namelen < 1 ) || ( namelen > 63 )) {
582 /* bytes 101 - 125 should be zero */
583 if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
586 /* macbinary forks aren't larger than 0x7FFFFF */
587 /* we allow forks to be larger, breaking the specs */
588 memcpy(&cc, head_buf + 83, sizeof(cc));
592 memcpy(&cc, head_buf + 87, sizeof(cc));
599 fprintf( stderr, "byte 82 is zero and name length is cool\n" );