• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.0/bin/megatron/
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