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