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