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