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