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