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