]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/asingle.c
megatron, fix errors introduced in Sun Feb 6 10:16:00 2005 commit, 'replace a wrong...
[netatalk.git] / bin / megatron / asingle.c
1 /*
2  * $Id: asingle.c,v 1.14 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 #ifdef HAVE_FCNTL_H
14 #include <fcntl.h>
15 #endif /* HAVE_FCNTL_H */
16 #include <time.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <stdio.h>
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif /* HAVE_UNISTD_H */
23 #include <atalk/adouble.h>
24 #include <netatalk/endian.h>
25 #include "asingle.h"
26 #include "megatron.h"
27
28 /*      String used to indicate standard input instead of a disk
29         file.  Should be a string not normally used for a file
30  */
31 #ifndef STDIN
32 #       define  STDIN   "-"
33 #endif
34
35 /*      Yes and no
36  */
37 #define NOWAY           0
38 #define SURETHANG       1
39
40 /*      This structure holds an entry description, consisting of three
41         four byte entities.  The first is the Entry ID, the second is
42         the File Offset and the third is the Length.
43  */
44
45
46 /*      Both input and output routines use this struct and the
47         following globals; therefore this module can only be used
48         for one of the two functions at a time.
49  */
50 static struct single_file_data {
51     int                 filed;
52     char                path[ MAXPATHLEN + 1];
53     struct ad_entry     entry[ ADEID_MAX ];
54 }               single;
55
56 extern char     *forkname[];
57 static u_char   header_buf[ AD_HEADER_LEN ];
58
59 /* 
60  * single_open must be called first.  pass it a filename that is supposed
61  * to contain a AppleSingle file.  an single struct will be allocated and
62  * somewhat initialized; single_filed is set.
63  */
64
65 int single_open(char *singlefile, int flags, struct FHeader *fh, int options _U_)
66 {
67     int                 rc;
68
69     if ( flags == O_RDONLY ) {
70         if ( strcmp( singlefile, STDIN ) == 0 ) {
71             single.filed = fileno( stdin );
72         } else if (( single.filed = open( singlefile, flags )) < 0 ) {
73             perror( singlefile );
74             return( -1 );
75         }
76         strncpy( single.path, singlefile, MAXPATHLEN );
77 #if DEBUG
78         fprintf( stderr, "opened %s for read\n", single.path );
79 #endif /* DEBUG */
80         if ((( rc = single_header_test()) > 0 ) && 
81                 ( single_header_read( fh, rc ) == 0 )) {
82             return( 0 );
83         }
84         single_close( KEEP );
85         return( -1 );
86     }
87     return( 0 );
88 }
89
90 /* 
91  * single_close must be called before a second file can be opened using
92  * single_open.  Upon successful completion, a value of 0 is returned.  
93  * Otherwise, a value of -1 is returned.
94  */
95
96 int single_close( int keepflag)
97 {
98     if ( keepflag == KEEP ) {
99         return( close( single.filed ));
100     } else if ( keepflag == TRASH ) {
101         if (( strcmp( single.path, STDIN ) != 0 ) && 
102                 ( unlink( single.path ) < 0 )) {
103             perror ( single.path );
104         }
105         return( 0 );
106     } else return( -1 );
107 }
108
109 /* 
110  * single_header_read is called by single_open, and before any information
111  * can read from the fh substruct.  it must be called before any of the
112  * bytes of the other two forks can be read, as well.
113  */
114
115 int single_header_read( struct FHeader *fh, int version)
116 {
117 /*
118  * entry_buf is used for reading in entry descriptors, and for reading in
119  *      the actual entries of FILEINFO, FINDERINFO, and DATES.
120  */
121     u_char              entry_buf[ 16 ];
122     u_int32_t           entry_id;
123     u_int32_t           time_seconds;
124     u_short             mask = 0xfcee;
125     u_short             num_entries;
126     int                 n;
127     int                 readlen;
128     int                 date_entry = 0;
129     off_t               pos;
130
131 /*
132  * Go through and initialize the array of entry_info structs.  Read in the
133  * number of entries, and then read in the info for each entry and save it
134  * in the array.
135  */
136
137     for ( n = 0 ; n < ADEID_MAX; n++ ) {
138         single.entry[ n ].ade_off = 0;
139         single.entry[ n ].ade_len = 0;
140     }
141     memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
142     num_entries = ntohs( num_entries );
143 #if DEBUG >= 2
144     fprintf( stderr, "The number of entries is %d\n", num_entries );
145 #endif /* DEBUG */
146     for ( ; num_entries > 0 ; num_entries-- ) {
147         if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
148                 != AD_ENTRY_LEN ) {
149             perror( "Premature end of file :" );
150             return( -1 );
151         }
152         memcpy(&entry_id,  entry_buf, sizeof( entry_id ));
153         entry_id = ntohl( entry_id );
154         memcpy(&single.entry[ entry_id ].ade_off,
155                entry_buf + 4,
156                sizeof( single.entry[ entry_id ].ade_off ));
157         single.entry[ entry_id ].ade_off =
158                 ntohl( single.entry[ entry_id ].ade_off );
159         memcpy(&single.entry[ entry_id ].ade_len,
160                entry_buf + 8,
161                sizeof( single.entry[ entry_id ].ade_len ));
162         single.entry[ entry_id ].ade_len =
163                 ntohl( single.entry[ entry_id ].ade_len );
164 #if DEBUG >= 2
165         fprintf( stderr, "entry_id\t%d\n", entry_id );
166         fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
167         fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
168 #endif /* DEBUG */
169     }
170
171 /*
172  * Now that the entries have been identified, check to make sure
173  * it is a Macintosh file if dealing with version two format file.
174  */
175
176     if ( version == 1 ) {
177         if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
178             date_entry = ADEID_FILEI;
179     } else if ( version == 2 ) {
180         if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
181             date_entry = ADEID_FILEDATESI;
182     }
183 #if DEBUG
184     fprintf( stderr, "date_entry = %d\n", date_entry );
185 #endif /* DEBUG */
186
187 /*
188  * Go through and copy all the information you can get from 
189  * the informational entries into the fh struct.  The ENTRYID_DATA
190  * must be the last one done, because it leaves the file pointer in
191  * the right place for the first read of the data fork.
192  */
193  
194     if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
195         fprintf( stderr, "%s has no name for the mac file.\n", single.path );
196         return( -1 );
197     } else {
198         pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
199                 SEEK_SET );
200         readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ? 
201               ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
202         if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
203             perror( "Premature end of file :" );
204             return( -1 );
205         }
206     }
207     if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
208             ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
209         fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
210         return( -1 );
211     } else {
212         pos = lseek( single.filed,
213                 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
214         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
215                 sizeof( entry_buf )) {
216             perror( "Premature end of file :" );
217             return( -1 );
218         }
219         memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,   
220                 sizeof( fh->finder_info.fdType ));
221         memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
222                 sizeof( fh->finder_info.fdCreator ));
223         memcpy( &fh->finder_info.fdFlags, entry_buf +  FINDERIOFF_FLAGS,
224                 sizeof( fh->finder_info.fdFlags ));
225         fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
226         memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
227                 sizeof( fh->finder_info.fdLocation ));
228         memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
229                 sizeof( fh->finder_info.fdFldr ));
230         fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
231         fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
232
233 #if DEBUG
234         {
235             char                type[5];
236             char                creator[5];
237  
238             strncpy( type, &fh->finder_info.fdType, 4 );
239             strncpy( creator, &fh->finder_info.fdCreator, 4 );
240             type[4] = creator[4] = '\0';
241             fprintf( stderr, "type is %s, creator is %s\n", type, creator );
242         }
243 #endif /* DEBUG */
244     }
245     if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) || 
246             ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
247         fh->comment[0] = '\0';
248     } else {
249         pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
250                 SEEK_SET );
251         readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
252                 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
253         if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
254             perror( "Premature end of file :" );
255             return( -1 );
256         }
257     }
258 /*
259  * If date_entry is 7, we have an AppleSingle version one, do the 
260  * appropriate stuff.  If it is 8, we have an AppleSingle version two,
261  * do the right thing.  If date_entry is neither, just use the current date.
262  * Unless I can't get the current date, in which case use time zero.
263  */
264     if (( date_entry < 7 ) || ( date_entry > 8 )) {
265         if (( time_seconds = time( NULL )) == (u_int32_t)-1 ) {
266             time_seconds = AD_DATE_START;
267         } else {
268             time_seconds = AD_DATE_FROM_UNIX(time_seconds);
269         }
270         memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
271         memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
272         fh->backup_date = AD_DATE_START;
273     } else if ( single.entry[ date_entry ].ade_len != 16 ) {
274         fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n", 
275                 single.path );
276         return( -1 );
277     } else if ( date_entry == ADEID_FILEI ) {
278         pos = lseek( single.filed,
279                 single.entry[ date_entry ].ade_off, SEEK_SET );
280         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
281                 sizeof( entry_buf )) {
282             perror( "Premature end of file :" );
283             return( -1 );
284         }
285         memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
286                 sizeof( fh->create_date ));
287         memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
288                 sizeof( fh->mod_date ));
289         memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
290                 sizeof(fh->backup_date));
291     } else if ( date_entry == ADEID_FILEDATESI ) {
292         pos = lseek( single.filed,
293                 single.entry[ date_entry ].ade_off, SEEK_SET );
294         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
295                 sizeof( entry_buf )) {
296             perror( "Premature end of file :" );
297             return( -1 );
298         }
299         memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
300                 sizeof( fh->create_date ));
301         memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
302                 sizeof( fh->mod_date ));
303         memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
304                 sizeof(fh->backup_date));
305     }
306     if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
307         fh->forklen[RESOURCE] = 0;
308     } else {
309         fh->forklen[RESOURCE] =
310                 htonl( single.entry[ ADEID_RFORK ].ade_len );
311     }
312     if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
313         fh->forklen[ DATA ] = 0;
314     } else {
315         fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
316         pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
317     }
318
319     return( 0 );
320 }
321
322 /*
323  * single_header_test is called from single_open.  It checks certain
324  * values of the file and determines if the file is an AppleSingle version
325  * one file something else, and returns a one, or negative one to indicate
326  * file type.
327  *
328  * The Magic Number of the file, the first four bytes, must be hex
329  * 0x00051600.  Bytes 4 through 7 are the version number and must be hex
330  * 0x00010000.  Bytes 8 through 23 identify the home file system, and we
331  * are only interested in files from Macs.  Therefore these bytes must
332  * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
333  * "Macintosh       " (that is seven blanks of padding).
334  */
335 #define MACINTOSH       "Macintosh       "
336 static u_char           sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
337                                     0, 0, 0, 0, 0, 0, 0, 0 };
338
339 int single_header_test(void)
340 {
341     ssize_t             cc;
342     u_int32_t           templong;
343
344     cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
345     if ( cc < (ssize_t)sizeof( header_buf )) {
346         perror( "Premature end of file :" );
347         return( -1 );
348     }
349
350     memcpy( &templong, header_buf, sizeof( templong ));
351     if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
352         fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
353         return( -1 );
354     }
355
356     memcpy(&templong,  header_buf +  4, sizeof( templong ));
357     templong = ntohl( templong );
358     if ( templong == AD_VERSION1 ) {
359         cc = 1;
360         if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 ) 
361                 != 0 ) {
362             fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n", 
363                     single.path );
364             return( -1 );
365         }
366     } else if ( templong == AD_VERSION2 ) {
367         cc = 2;
368         if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
369                 != 0 ) {
370             fprintf( stderr, 
371                     "Warning:  %s may be a corrupt AppleSingle file.\n",
372                     single.path );
373             return( -1 );
374         }
375     } else {
376         fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
377                 single.path );
378         return( -1 );
379     }
380
381     return( cc );
382 }
383
384 /*
385  * single_read is called until it returns zero for each fork.  When
386  * it returns zero for the first fork, it seeks to the proper place
387  * to read in the next, if there is one.  single_read must be called
388  * enough times to return zero for each fork and no more.
389  *
390  */
391
392 ssize_t single_read( int fork, char *buffer, size_t length)
393 {
394     u_int32_t           entry_id;
395     char                *buf_ptr;
396     size_t              readlen;
397     ssize_t             cc = 1;
398     off_t               pos;
399
400     switch ( fork ) {
401         case DATA :
402             entry_id = ADEID_DFORK;
403             break;
404         case RESOURCE :
405             entry_id = ADEID_RFORK;
406             break;
407         default :
408             return( -1 );
409             break;
410     }
411
412     if (single.entry[entry_id].ade_len > 0x7FFFFFFF) {
413         fprintf(stderr, "single_read: Trying to read past end of fork!, ade_len == %u\n", single.entry[entry_id].ade_len);
414         return -1;
415     }
416     if ( single.entry[ entry_id ].ade_len == 0 ) {
417         if ( fork == DATA ) {
418             pos = lseek( single.filed,
419                 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
420         }
421         return( 0 );
422     }
423
424     if ( single.entry[ entry_id ].ade_len < length ) {
425         readlen = single.entry[ entry_id ].ade_len;
426     } else {
427         readlen = length;
428     }
429
430     buf_ptr = buffer;
431     while (( readlen > 0 ) && ( cc > 0 )) {
432         if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
433             readlen -= cc;
434             buf_ptr += cc;
435         }
436     }
437     if ( cc >= 0 ) {
438         cc = buf_ptr - buffer;
439         single.entry[ entry_id ].ade_len -= cc;
440     }
441
442     return( cc );
443 }