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