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