]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/macbin.c
1b2dd197a665efeba762772576fe600a0c35ca0d
[netatalk.git] / bin / megatron / macbin.c
1 /*
2  * $Id: macbin.c,v 1.14 2009-10-14 01:38:28 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  */
56 static 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
64 extern char     *forkname[];
65 static 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
73 int 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
146 int 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
170 int bin_read( int fork, char *buffer, int length)
171 {
172     char                *buf_ptr;
173     int                 readlen;
174     int                 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] > length) {
183         fprintf(stderr, "This should never happen, dude! length %d, fork length == %u\n", length, bin.forklen[fork]);
184         return bin.forklen[fork];
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
239 int bin_write(int fork, char *buffer, int length)
240 {
241     char                *buf_ptr;
242     int                 writelen;
243     int                 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     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];
284     }
285
286     bin.forklen[fork] -= length;
287
288 /*
289  * add the padding at end of data and resource forks
290  */
291
292     if ( bin.forklen[ fork ] == 0 ) {
293         pos = lseek( bin.filed, 0, SEEK_CUR );
294 #if DEBUG
295         fprintf( stderr, "current position is %ld\n", pos );
296 #endif /* DEBUG */
297         pos %= HEADBUFSIZ;
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:" );
302             return( -1 );
303           }
304         }
305 #if DEBUG
306           fprintf( stderr, "current position is %ld\n", pos );
307 #endif /* DEBUG */
308     }
309
310 #if DEBUG
311         fprintf( stderr, "\n" );
312 #endif /* DEBUG */
313
314     return( length );
315 }
316
317 /* 
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.
321  */
322
323 int bin_header_read(struct FHeader *fh, int revision)
324 {
325     u_short             mask;
326
327 /*
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
331  * negative.
332  */
333
334     switch ( revision ) {
335         case 3:
336         case 2 :
337             mask = htons( 0xfcee );
338             memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
339             break;
340         case 1 :
341             mask = htons( 0xfc00 );
342             break;
343         default :
344             return( -1 );
345             break;
346     }
347
348 /*
349  * Go through and copy all the stuff you can get from the 
350  * MacBinary header into the fh struct.  What fun!
351  */
352
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 );
362
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 */
369
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';
377
378     if (revision == 3) {
379       fh->finder_xinfo.fdScript = *(head_buf + 106);
380       fh->finder_xinfo.fdXFlags = *(head_buf + 107);
381     }
382
383 #if DEBUG >= 5
384     {
385         short           flags;
386         long            flags_long;
387
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 );
400
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 );
406
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" );
410     }
411 #endif /* DEBUG >= 5 */
412
413     return( 0 );
414 }
415
416 /* 
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.
421  */
422
423 int bin_header_write(struct FHeader *fh)
424 {
425     char                *write_ptr;
426     u_int32_t           t;
427     int                 wc;
428     int                 wr;
429
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 );
434
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 */
440
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);
452
453     /* macbinary III */
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;
458
459     head_buf[ 123 ] = 129;
460
461     bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
462     memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
463
464     bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
465     bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
466
467 #if DEBUG >= 5
468     {
469         short   flags;
470         long    flags_long;
471
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 ] );
477
478         memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
479         flags = ntohs( flags );
480         fprintf( stderr, "flags\t\t\t%x\n", flags );
481
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 );
487
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" );
491     }
492 #endif /* DEBUG >= 5 */
493
494     write_ptr = (char *)head_buf;
495     wc = sizeof( head_buf );
496     wr = 0;
497     while (( wc > 0 ) && ( wr >= 0 )) {
498         wr = write( bin.filed, write_ptr, wc );
499         write_ptr += wr;
500         wc -= wr;
501     }
502     if ( wr < 0 ) {
503         perror( "Couldn't write macbinary header:" );
504         return( wr );
505     }
506
507     return( 0 );
508 }
509
510 /*
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.
515  *
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. 
523  *
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.
526  */
527
528 int test_header(void)
529 {
530     const char          zeros[25] = "";
531     u_int32_t           cc;
532     u_short             header_crc;
533     u_char              namelen;
534
535 #if DEBUG
536     fprintf( stderr, "entering test_header\n" );
537 #endif /* DEBUG */
538
539     cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
540     if ( cc < sizeof( head_buf )) {
541         perror( "Premature end of file :" );
542         return( -1 );
543     }
544
545 #if DEBUG
546     fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
547 #endif /* DEBUG */
548
549     /* check for macbinary III header */
550     if (memcmp(head_buf + 102, "mBIN", 4) == 0)
551         return 3;
552
553     /* check for macbinary II even if only one of the bytes is zero */
554     if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
555 #if DEBUG
556       fprintf( stderr, "byte 0 and 74 are both zero\n" );
557 #endif /* DEBUG */
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 ) {
562         return( 2 );
563       }
564
565 #if DEBUG
566       fprintf( stderr, "header crc didn't pan out\n" );
567 #endif /* DEBUG */
568     }
569
570     /* now see if we have a macbinary file. */
571     if ( head_buf[ 82 ] != 0 ) {
572         return( -1 );
573     }
574     memcpy( &namelen, head_buf + 1, sizeof( namelen ));
575 #if DEBUG
576     fprintf( stderr, "name length is %d\n", namelen );
577 #endif /* DEBUG */
578     if (( namelen < 1 ) || ( namelen > 63 )) {
579         return( -1 );
580     }
581
582     /* bytes 101 - 125 should be zero */
583     if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
584         return -1;
585
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));
589     cc = ntohl(cc);
590     if (cc > 0x7FFFFFFF)
591         return -1;
592     memcpy(&cc, head_buf + 87, sizeof(cc));
593     cc = ntohl(cc);
594     if (cc > 0x7FFFFFFF)
595         return -1;
596
597
598 #if DEBUG
599     fprintf( stderr, "byte 82 is zero and name length is cool\n" );
600 #endif /* DEBUG */
601
602     return( 1 );
603 }