• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/bin/megatron/
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 */
50static struct single_file_data {
51    int			filed;
52    char		path[ MAXPATHLEN + 1];
53    struct ad_entry	entry[ ADEID_MAX ];
54} 		single;
55
56extern char	*forkname[];
57static 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
65int 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
96int 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
115int 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       "
335static u_char		sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
336				    0, 0, 0, 0, 0, 0, 0, 0 };
337
338int 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
391ssize_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}
443