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