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