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