2
* $Id: asingle.c,v 1.8.4.1 2003/08/09 14:30:21 srittau Exp $
7
#endif /* HAVE_CONFIG_H */
12
#include <sys/param.h>
15
#endif /* HAVE_FCNTL_H */
22
#endif /* HAVE_UNISTD_H */
23
#include <atalk/adouble.h>
24
#include <netatalk/endian.h>
28
/* String used to indicate standard input instead of a disk
29
file. Should be a string not normally used for a file
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.
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.
50
struct single_file_data {
52
char path[ MAXPATHLEN + 1];
53
struct ad_entry entry[ ADEID_MAX ];
56
extern char *forkname[];
57
u_char header_buf[ AD_HEADER_LEN ];
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.
65
int single_open( singlefile, flags, fh, options )
72
if ( flags == O_RDONLY ) {
73
if ( strcmp( singlefile, STDIN ) == 0 ) {
74
single.filed = fileno( stdin );
75
} else if (( single.filed = open( singlefile, flags )) < 0 ) {
79
strncpy( single.path, singlefile, MAXPATHLEN );
81
fprintf( stderr, "opened %s for read\n", single.path );
83
if ((( rc = single_header_test()) > 0 ) &&
84
( single_header_read( fh, rc ) == 0 )) {
94
* single_close must be called before a second file can be opened using
95
* single_open. Upon successful completion, a value of 0 is returned.
96
* Otherwise, a value of -1 is returned.
99
int single_close( keepflag )
102
if ( keepflag == KEEP ) {
103
return( close( single.filed ));
104
} else if ( keepflag == TRASH ) {
105
if (( strcmp( single.path, STDIN ) != 0 ) &&
106
( unlink( single.path ) < 0 )) {
107
perror ( single.path );
114
* single_header_read is called by single_open, and before any information
115
* can read from the fh substruct. it must be called before any of the
116
* bytes of the other two forks can be read, as well.
119
int single_header_read( fh, version )
124
* entry_buf is used for reading in entry descriptors, and for reading in
125
* the actual entries of FILEINFO, FINDERINFO, and DATES.
127
u_char entry_buf[ 16 ];
129
u_int32_t time_seconds;
130
u_short mask = 0xfcee;
138
* Go through and initialize the array of entry_info structs. Read in the
139
* number of entries, and then read in the info for each entry and save it
143
for ( n = 0 ; n < ADEID_MAX; n++ ) {
144
single.entry[ n ].ade_off = 0;
145
single.entry[ n ].ade_len = 0;
147
memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
148
num_entries = ntohs( num_entries );
150
fprintf( stderr, "The number of entries is %d\n", num_entries );
152
for ( ; num_entries > 0 ; num_entries-- ) {
153
if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
155
perror( "Premature end of file :" );
158
memcpy(&entry_id, entry_buf, sizeof( entry_id ));
159
entry_id = ntohl( entry_id );
160
memcpy(&single.entry[ entry_id ].ade_off,
162
sizeof( single.entry[ entry_id ].ade_off ));
163
single.entry[ entry_id ].ade_off =
164
ntohl( single.entry[ entry_id ].ade_off );
165
memcpy(&single.entry[ entry_id ].ade_len,
167
sizeof( single.entry[ entry_id ].ade_len ));
168
single.entry[ entry_id ].ade_len =
169
ntohl( single.entry[ entry_id ].ade_len );
171
fprintf( stderr, "entry_id\t%d\n", entry_id );
172
fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
173
fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
178
* Now that the entries have been identified, check to make sure
179
* it is a Macintosh file if dealing with version two format file.
182
if ( version == 1 ) {
183
if ( single.entry[ ADEID_FILEI ].ade_len > 0 )
184
date_entry = ADEID_FILEI;
185
} else if ( version == 2 ) {
186
if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 )
187
date_entry = ADEID_FILEDATESI;
190
fprintf( stderr, "date_entry = %d\n", date_entry );
194
* Go through and copy all the information you can get from
195
* the informational entries into the fh struct. The ENTRYID_DATA
196
* must be the last one done, because it leaves the file pointer in
197
* the right place for the first read of the data fork.
200
if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
201
fprintf( stderr, "%s has no name for the mac file.\n", single.path );
204
pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
206
readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
207
ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
208
if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
209
perror( "Premature end of file :" );
213
if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
214
( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
215
fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
218
pos = lseek( single.filed,
219
single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
220
if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
221
sizeof( entry_buf )) {
222
perror( "Premature end of file :" );
225
memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
226
sizeof( fh->finder_info.fdType ));
227
memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
228
sizeof( fh->finder_info.fdCreator ));
229
memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
230
sizeof( fh->finder_info.fdFlags ));
231
fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
232
memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
233
sizeof( fh->finder_info.fdLocation ));
234
memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
235
sizeof( fh->finder_info.fdFldr ));
236
fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
237
fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
244
strncpy( type, &fh->finder_info.fdType, 4 );
245
strncpy( creator, &fh->finder_info.fdCreator, 4 );
246
type[4] = creator[4] = '\0';
247
fprintf( stderr, "type is %s, creator is %s\n", type, creator );
251
if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
252
( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
253
fh->comment[0] = '\0';
255
pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
257
readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
258
? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
259
if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
260
perror( "Premature end of file :" );
265
* If date_entry is 7, we have an AppleSingle version one, do the
266
* appropriate stuff. If it is 8, we have an AppleSingle version two,
267
* do the right thing. If date_entry is neither, just use the current date.
268
* Unless I can't get the current date, in which case use time zero.
270
if (( date_entry < 7 ) || ( date_entry > 8 )) {
271
if (( time_seconds = time( NULL )) == -1 ) {
272
time_seconds = AD_DATE_START;
274
time_seconds = AD_DATE_FROM_UNIX(time_seconds);
276
memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
277
memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
278
fh->backup_date = AD_DATE_START;
279
} else if ( single.entry[ date_entry ].ade_len != 16 ) {
280
fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
283
} else if ( date_entry == ADEID_FILEI ) {
284
pos = lseek( single.filed,
285
single.entry[ date_entry ].ade_off, SEEK_SET );
286
if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
287
sizeof( entry_buf )) {
288
perror( "Premature end of file :" );
291
memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
292
sizeof( fh->create_date ));
293
memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
294
sizeof( fh->mod_date ));
295
memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
296
sizeof(fh->backup_date));
297
} else if ( date_entry == ADEID_FILEDATESI ) {
298
pos = lseek( single.filed,
299
single.entry[ date_entry ].ade_off, SEEK_SET );
300
if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
301
sizeof( entry_buf )) {
302
perror( "Premature end of file :" );
305
memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
306
sizeof( fh->create_date ));
307
memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
308
sizeof( fh->mod_date ));
309
memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
310
sizeof(fh->backup_date));
312
if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
313
fh->forklen[ ADEID_RFORK ] = 0;
315
fh->forklen[ ADEID_RFORK ] =
316
htonl( single.entry[ ADEID_RFORK ].ade_len );
318
if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
319
fh->forklen[ DATA ] = 0;
321
fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
322
pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
329
* single_header_test is called from single_open. It checks certain
330
* values of the file and determines if the file is an AppleSingle version
331
* one file something else, and returns a one, or negative one to indicate
334
* The Magic Number of the file, the first four bytes, must be hex
335
* 0x00051600. Bytes 4 through 7 are the version number and must be hex
336
* 0x00010000. Bytes 8 through 23 identify the home file system, and we
337
* are only interested in files from Macs. Therefore these bytes must
338
* contain hex 0x4d6163696e746f736820202020202020 which is ASCII
339
* "Macintosh " (that is seven blanks of padding).
341
#define MACINTOSH "Macintosh "
342
u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
343
0, 0, 0, 0, 0, 0, 0, 0 };
345
int single_header_test(void)
350
cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
351
if ( cc < sizeof( header_buf )) {
352
perror( "Premature end of file :" );
356
memcpy( &templong, header_buf, sizeof( templong ));
357
if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
358
fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
362
memcpy(&templong, header_buf + 4, sizeof( templong ));
363
templong = ntohl( templong );
364
if ( templong == AD_VERSION1 ) {
366
if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
368
fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
372
} else if ( templong == AD_VERSION2 ) {
374
if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
377
"Warning: %s may be a corrupt AppleSingle file.\n",
382
fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
391
* single_read is called until it returns zero for each fork. When
392
* it returns zero for the first fork, it seeks to the proper place
393
* to read in the next, if there is one. single_read must be called
394
* enough times to return zero for each fork and no more.
398
int single_read( fork, buffer, length )
411
entry_id = ADEID_DFORK;
414
entry_id = ADEID_RFORK;
421
if ( single.entry[ entry_id ].ade_len < 0 ) {
422
fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
423
return( single.entry[ entry_id ].ade_len );
425
if ( single.entry[ entry_id ].ade_len == 0 ) {
426
if ( fork == DATA ) {
427
pos = lseek( single.filed,
428
single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
433
if ( single.entry[ entry_id ].ade_len < length ) {
434
readlen = single.entry[ entry_id ].ade_len;
440
while (( readlen > 0 ) && ( cc > 0 )) {
441
if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
447
cc = buf_ptr - buffer;
448
single.entry[ entry_id ].ade_len -= cc;