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