]> arthur.barton.de Git - netatalk.git/blob - bin/megatron/asingle.c
From trunk: remove inclusion of syslog.h.
[netatalk.git] / bin / megatron / asingle.c
1 /*
2  * $Id: asingle.c,v 1.6.2.1 2002-03-12 11:50:54 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;
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 {
186             date_entry = 0;
187         }
188     } else if ( version == 2 ) {
189         if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
190             date_entry = ADEID_FILEDATESI;
191         } else {
192             date_entry = 0;
193         }
194     }
195 #if DEBUG
196     fprintf( stderr, "date_entry = %d\n", date_entry );
197 #endif /* DEBUG */
198
199 /*
200  * Go through and copy all the information you can get from 
201  * the informational entries into the fh struct.  The ENTRYID_DATA
202  * must be the last one done, because it leaves the file pointer in
203  * the right place for the first read of the data fork.
204  */
205  
206     if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
207         fprintf( stderr, "%s has no name for the mac file.\n", single.path );
208         return( -1 );
209     } else {
210         pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
211                 SEEK_SET );
212         readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ? 
213               ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
214         if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
215             perror( "Premature end of file :" );
216             return( -1 );
217         }
218     }
219     if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
220             ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
221         fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
222         return( -1 );
223     } else {
224         pos = lseek( single.filed,
225                 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
226         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
227                 sizeof( entry_buf )) {
228             perror( "Premature end of file :" );
229             return( -1 );
230         }
231         memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,   
232                 sizeof( fh->finder_info.fdType ));
233         memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
234                 sizeof( fh->finder_info.fdCreator ));
235         memcpy( &fh->finder_info.fdFlags, entry_buf +  FINDERIOFF_FLAGS,
236                 sizeof( fh->finder_info.fdFlags ));
237         fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
238         memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
239                 sizeof( fh->finder_info.fdLocation ));
240         memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
241                 sizeof( fh->finder_info.fdFldr ));
242         fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
243         fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
244
245 #if DEBUG
246         {
247             char                type[5];
248             char                creator[5];
249  
250             strncpy( type, &fh->finder_info.fdType, 4 );
251             strncpy( creator, &fh->finder_info.fdCreator, 4 );
252             type[4] = creator[4] = '\0';
253             fprintf( stderr, "type is %s, creator is %s\n", type, creator );
254         }
255 #endif /* DEBUG */
256     }
257     if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) || 
258             ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
259         fh->comment[0] = '\0';
260     } else {
261         pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
262                 SEEK_SET );
263         readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
264                 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
265         if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
266             perror( "Premature end of file :" );
267             return( -1 );
268         }
269     }
270 /*
271  * If date_entry is 7, we have an AppleSingle version one, do the 
272  * appropriate stuff.  If it is 8, we have an AppleSingle version two,
273  * do the right thing.  If date_entry is neither, just use the current date.
274  * Unless I can't get the current date, in which case use time zero.
275  */
276     if (( date_entry < 7 ) || ( date_entry > 8 )) {
277         if (( time_seconds = time( NULL )) == -1 ) {
278             time_seconds = AD_DATE_START;
279         } else {
280             time_seconds = AD_DATE_FROM_UNIX(time_seconds);
281         }
282         memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
283         memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
284         fh->backup_date = AD_DATE_START;
285     } else if ( single.entry[ date_entry ].ade_len != 16 ) {
286         fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n", 
287                 single.path );
288         return( -1 );
289     } else if ( date_entry == ADEID_FILEI ) {
290         pos = lseek( single.filed,
291                 single.entry[ date_entry ].ade_off, SEEK_SET );
292         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
293                 sizeof( entry_buf )) {
294             perror( "Premature end of file :" );
295             return( -1 );
296         }
297         memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
298                 sizeof( fh->create_date ));
299         memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
300                 sizeof( fh->mod_date ));
301         memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
302                 sizeof(fh->backup_date));
303     } else if ( date_entry == ADEID_FILEDATESI ) {
304         pos = lseek( single.filed,
305                 single.entry[ date_entry ].ade_off, SEEK_SET );
306         if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
307                 sizeof( entry_buf )) {
308             perror( "Premature end of file :" );
309             return( -1 );
310         }
311         memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
312                 sizeof( fh->create_date ));
313         memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
314                 sizeof( fh->mod_date ));
315         memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
316                 sizeof(fh->backup_date));
317     }
318     if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
319         fh->forklen[ ADEID_RFORK ] = 0;
320     } else {
321         fh->forklen[ ADEID_RFORK ] =
322                 htonl( single.entry[ ADEID_RFORK ].ade_len );
323     }
324     if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
325         fh->forklen[ DATA ] = 0;
326     } else {
327         fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
328         pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
329     }
330
331     return( 0 );
332 }
333
334 /*
335  * single_header_test is called from single_open.  It checks certain
336  * values of the file and determines if the file is an AppleSingle version
337  * one file something else, and returns a one, or negative one to indicate
338  * file type.
339  *
340  * The Magic Number of the file, the first four bytes, must be hex
341  * 0x00051600.  Bytes 4 through 7 are the version number and must be hex
342  * 0x00010000.  Bytes 8 through 23 identify the home file system, and we
343  * are only interested in files from Macs.  Therefore these bytes must
344  * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
345  * "Macintosh       " (that is seven blanks of padding).
346  */
347 #define MACINTOSH       "Macintosh       "
348 u_char          sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
349                                     0, 0, 0, 0, 0, 0, 0, 0 };
350
351 int single_header_test(void)
352 {
353     int                 cc;
354     u_int32_t           templong;
355
356     cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
357     if ( cc < sizeof( header_buf )) {
358         perror( "Premature end of file :" );
359         return( -1 );
360     }
361
362     memcpy( &templong, header_buf, sizeof( templong ));
363     if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
364         fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
365         return( -1 );
366     }
367
368     memcpy(&templong,  header_buf +  4, sizeof( templong ));
369     templong = ntohl( templong );
370     if ( templong == AD_VERSION1 ) {
371         cc = 1;
372         if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 ) 
373                 != 0 ) {
374             fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n", 
375                     single.path );
376             return( -1 );
377         }
378     } else if ( templong == AD_VERSION2 ) {
379         cc = 2;
380         if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
381                 != 0 ) {
382             fprintf( stderr, 
383                     "Warning:  %s may be a corrupt AppleSingle file.\n",
384                     single.path );
385             return( -1 );
386         }
387     } else {
388         fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
389                 single.path );
390         return( -1 );
391     }
392
393     return( cc );
394 }
395
396 /*
397  * single_read is called until it returns zero for each fork.  When
398  * it returns zero for the first fork, it seeks to the proper place
399  * to read in the next, if there is one.  single_read must be called
400  * enough times to return zero for each fork and no more.
401  *
402  */
403
404 int single_read( fork, buffer, length )
405     int                 fork;
406     char                *buffer;
407     int                 length;
408 {
409     u_int32_t           entry_id;
410     char                *buf_ptr;
411     int                 readlen;
412     int                 cc = 1;
413     off_t               pos;
414
415     switch ( fork ) {
416         case DATA :
417             entry_id = ADEID_DFORK;
418             break;
419         case RESOURCE :
420             entry_id = ADEID_RFORK;
421             break;
422         default :
423             return( -1 );
424             break;
425     }
426
427     if ( single.entry[ entry_id ].ade_len < 0 ) {
428         fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
429         return( single.entry[ entry_id ].ade_len );
430     }
431     if ( single.entry[ entry_id ].ade_len == 0 ) {
432         if ( fork == DATA ) {
433             pos = lseek( single.filed,
434                 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
435         }
436         return( 0 );
437     }
438
439     if ( single.entry[ entry_id ].ade_len < length ) {
440         readlen = single.entry[ entry_id ].ade_len;
441     } else {
442         readlen = length;
443     }
444
445     buf_ptr = buffer;
446     while (( readlen > 0 ) && ( cc > 0 )) {
447         if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
448             readlen -= cc;
449             buf_ptr += cc;
450         }
451     }
452     if ( cc >= 0 ) {
453         cc = buf_ptr - buffer;
454         single.entry[ entry_id ].ade_len -= cc;
455     }
456
457     return( cc );
458 }